rnatm

Load and structure data describing an atmosphere
git clone git://git.meso-star.fr/rnatm.git
Log | Files | Refs | README | LICENSE

commit 4b471ba6411378dec31ce2bbd575afc0c8af04e8
parent 6c8ce86b883bdceaf0aef71f2042ab0581b9a249
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Thu, 28 Jul 2022 15:28:11 +0200

Updating the partition voxel management

The caller can release voxels from a partition. In this case, the
partition is considered empty, that is, it contains only empty voxels.
Also, we now allow more partitions than the number of non-empty
partitions. Thus, we can reduce thread contention without significantly
increasing memory usage, when threads are waiting for a non-empty
partition while other threads are handling empty partitions.

Diffstat:
Msrc/rnatm_octree.c | 152++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/rnatm_voxel_partition.c | 374+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/rnatm_voxel_partition.h | 43+++++++++++++++++++++++++++++--------------
3 files changed, 357 insertions(+), 212 deletions(-)

diff --git a/src/rnatm_octree.c b/src/rnatm_octree.c @@ -48,6 +48,12 @@ struct build_octrees_context { static const struct build_octrees_context BUILD_OCTREES_CONTEXT_NULL = BUILD_OCTREES_CONTEXT_NULL__; +struct radcoefs { + float ka_min, ka_max; + float ks_min, ks_max; + float kext_min, kext_max; +}; + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -132,61 +138,63 @@ compute_grid_definition(struct rnatm* atm, const struct rnatm_create_args* args) return RES_OK; } -static res_T -update_voxel +static INLINE void +setup_tetra_radcoefs (struct rnatm* atm, const struct suvm_primitive* tetra, - struct partition* part, const size_t iband, const size_t iquad_pt, - const uint64_t vx_mcode) + struct radcoefs* radcoefs) { + float ka[4]; + float ks[4]; + float kext[4]; struct sck_band band; struct sck_quad_pt quad_pt; - - float tetra_ks[4]; - float tetra_ka[4]; - float tetra_kext[4]; - float tetra_ks_min, tetra_ks_max; - float tetra_ka_min, tetra_ka_max; - float tetra_kext_min, tetra_kext_max; - - float vx_ka_min, vx_ka_max; - float vx_ks_min, vx_ks_max; - float vx_kext_min, vx_kext_max; - - float* vx = NULL; - ASSERT(atm && tetra && part); + ASSERT(atm && tetra && radcoefs); ASSERT(tetra->nvertices == 4); - #define REDUX_MIN(V4) MMIN(MMIN((V4)[0], (V4)[1]), MMIN((V4)[2], V4[3])) - #define REDUX_MAX(V4) MMAX(MMAX((V4)[0], (V4)[1]), MMAX((V4)[2], V4[3])) - /* Compute the scattering coefficient range of the tetrahedron */ SCK(get_band(atm->gas.ck, iband, &band)); - tetra_ks[0] = band.ks_list[tetra->indices[0]]; - tetra_ks[1] = band.ks_list[tetra->indices[1]]; - tetra_ks[2] = band.ks_list[tetra->indices[2]]; - tetra_ks[3] = band.ks_list[tetra->indices[3]]; - tetra_ks_min = REDUX_MIN(tetra_ks); - tetra_ks_max = REDUX_MAX(tetra_ks); + ks[0] = band.ks_list[tetra->indices[0]]; + ks[1] = band.ks_list[tetra->indices[1]]; + ks[2] = band.ks_list[tetra->indices[2]]; + ks[3] = band.ks_list[tetra->indices[3]]; + radcoefs->ks_min = MMIN(MMIN(ks[0], ks[1]), MMIN(ks[2], ks[3])); + radcoefs->ks_max = MMAX(MMAX(ks[0], ks[1]), MMAX(ks[2], ks[3])); /* Compute the absorption coefficient range of the tetrahedron */ SCK(band_get_quad_pt(&band, iquad_pt, &quad_pt)); - tetra_ka[0] = quad_pt.ka_list[tetra->indices[0]]; - tetra_ka[1] = quad_pt.ka_list[tetra->indices[1]]; - tetra_ka[2] = quad_pt.ka_list[tetra->indices[2]]; - tetra_ka[3] = quad_pt.ka_list[tetra->indices[3]]; - tetra_ka_min = REDUX_MIN(tetra_ka); - tetra_ka_max = REDUX_MAX(tetra_ka); + ka[0] = quad_pt.ka_list[tetra->indices[0]]; + ka[1] = quad_pt.ka_list[tetra->indices[1]]; + ka[2] = quad_pt.ka_list[tetra->indices[2]]; + ka[3] = quad_pt.ka_list[tetra->indices[3]]; + radcoefs->ka_min = MMIN(MMIN(ka[0], ka[1]), MMIN(ka[2], ka[3])); + radcoefs->ka_max = MMAX(MMAX(ka[0], ka[1]), MMAX(ka[2], ka[3])); /* Compute the extinction coefficient range of the tetrahedron */ - tetra_kext[0] = tetra_ka[0] + tetra_ks[0]; - tetra_kext[1] = tetra_ka[1] + tetra_ks[1]; - tetra_kext[2] = tetra_ka[2] + tetra_ks[2]; - tetra_kext[3] = tetra_ka[3] + tetra_ks[3]; - tetra_kext_min = REDUX_MIN(tetra_kext); - tetra_kext_max = REDUX_MAX(tetra_kext); + kext[0] = ka[0] + ks[0]; + kext[1] = ka[1] + ks[1]; + kext[2] = ka[2] + ks[2]; + kext[3] = ka[3] + ks[3]; + radcoefs->kext_min = MMIN(MMIN(kext[0], kext[1]), MMIN(kext[2], kext[3])); + radcoefs->kext_max = MMAX(MMAX(kext[0], kext[1]), MMAX(kext[2], kext[3])); +} + +static INLINE void +update_voxel + (struct rnatm* atm, + const struct radcoefs* radcoefs, + struct partition* part, + const uint64_t vx_mcode) +{ + float vx_ka_min, vx_ka_max; + float vx_ks_min, vx_ks_max; + float vx_kext_min, vx_kext_max; + + float* vx = NULL; + ASSERT(atm && radcoefs && part); + (void)atm; vx = partition_get_voxel(part, vx_mcode); @@ -197,23 +205,18 @@ update_voxel vx_ks_max = vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MAX)]; vx_kext_min = vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MIN)]; vx_kext_max = vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MAX)]; - vx_ka_min = MMIN(vx_ka_min, tetra_ka_min); - vx_ka_max = MMAX(vx_ka_max, tetra_ka_max); - vx_ks_min = MMIN(vx_ks_min, tetra_ks_min); - vx_ks_max = MMAX(vx_ks_max, tetra_ks_max); - vx_kext_min = MMIN(vx_kext_min, tetra_kext_min); - vx_kext_max = MMAX(vx_kext_max, tetra_kext_max); + vx_ka_min = MMIN(vx_ka_min, radcoefs->ka_min); + vx_ka_max = MMAX(vx_ka_max, radcoefs->ka_max); + vx_ks_min = MMIN(vx_ks_min, radcoefs->ks_min); + vx_ks_max = MMAX(vx_ks_max, radcoefs->ks_max); + vx_kext_min = MMIN(vx_kext_min, radcoefs->kext_min); + vx_kext_max = MMAX(vx_kext_max, radcoefs->kext_max); vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MIN)] = vx_ka_min; vx[voxel_idata(RNATM_RADCOEF_Ka, RNATM_SVX_OP_MAX)] = vx_ka_max; vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MIN)] = vx_ks_min; vx[voxel_idata(RNATM_RADCOEF_Ks, RNATM_SVX_OP_MAX)] = vx_ks_max; vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MIN)] = vx_kext_min; vx[voxel_idata(RNATM_RADCOEF_Kext, RNATM_SVX_OP_MAX)] = vx_kext_max; - - #undef REDUX_MIN - #undef REDUX_MAX - - return RES_OK; } static res_T @@ -226,7 +229,6 @@ voxelize_gas struct partition* part) { size_t i; - res_T res = RES_OK; ASSERT(atm && part_low && part_upp && tetra_ids && part); ASSERT(vxsz[0] > 0 && vxsz[1] > 0 && vxsz[2] > 0); ASSERT(part_low[0] < part_upp[0]); @@ -237,6 +239,7 @@ voxelize_gas FOR_EACH(i, 0, darray_size_t_size_get(tetra_ids)) { struct suvm_primitive tetra; + struct radcoefs radcoefs; struct suvm_polyhedron poly; double poly_low[3]; double poly_upp[3]; @@ -275,6 +278,10 @@ voxelize_gas ASSERT(ivx_upp[1] <= partition_get_definition(part)); ASSERT(ivx_upp[2] <= partition_get_definition(part)); + /* Compute the range of the tetrahedron radiative coefficients. + * TODO setup the band and quadrature point */ + setup_tetra_radcoefs(atm, &tetra, 0/*iband*/, 0/*iquad*/, &radcoefs); + /* Iterate voxels intersected by the AABB of the polyedron */ FOR_EACH(ivx[2], ivx_low[2], ivx_upp[2]) { vx_low[2] = (float)((double)ivx[2]*vxsz[2] + part_low[2]); @@ -294,18 +301,13 @@ voxelize_gas intersect = suvm_polyhedron_intersect_aabb(&poly, vx_low, vx_upp); if(intersect == SUVM_INTERSECT_NONE) continue; - /* TODO setup the band and quadrature point */ - res = update_voxel(atm, &tetra, part, 0/*iband*/, 0/*iquad*/, mcode[0]); - if(res != RES_OK) goto error; + update_voxel(atm, &radcoefs, part, mcode[0]); } } } } -exit: - return res; -error: - partition_clear_voxels(part); - goto exit; + + return RES_OK; } static res_T @@ -329,6 +331,12 @@ voxelize_partition (atm->gas.volume, part_low, part_upp, register_tetra, tetra_ids); if(res != RES_OK) goto error; + /* The partition is not covered by any tetrahedron */ + if(darray_size_t_size_get(tetra_ids) == 0) { + partition_empty(part); + goto exit; + } + res = voxelize_gas(atm, part_low, part_upp, vxsz, tetra_ids, part); if(res != RES_OK) goto error; @@ -411,7 +419,7 @@ voxelize_atmosphere(struct rnatm* atm, struct pool* pool) if(part_ids[0] >= nparts[0] || part_ids[1] >= nparts[1] || part_ids[2] >= nparts[2]) { - pool_free_partition(pool, part); + partition_free(part); continue; } @@ -431,9 +439,9 @@ voxelize_atmosphere(struct rnatm* atm, struct pool* pool) res_local = voxelize_partition (atm, part_low, part_upp, vxsz, tetra_ids, part); if(res_local == RES_OK) { - pool_commit_partition(pool, part); + partition_commit(part); } else { - pool_free_partition(pool, part); + partition_free(part); ATOMIC_SET(&res, res_local); continue; }; @@ -464,7 +472,7 @@ static void vx_get(const size_t xyz[3], const uint64_t mcode, void* dst, void* context) { struct build_octrees_context* ctx = context; - float* vx = NULL; + const float* vx = NULL; uint64_t ivx, ipart; int log2_part_def; ASSERT(xyz && dst && ctx); @@ -482,7 +490,7 @@ vx_get(const size_t xyz[3], const uint64_t mcode, void* dst, void* context) /* Recover the partition storing the voxel */ if(ctx->part == NULL || partition_get_id(ctx->part) != ipart) { - if(ctx->part) pool_free_partition(ctx->pool, ctx->part); + if(ctx->part) partition_free(ctx->part); ctx->part = pool_fetch_partition(ctx->pool, ipart); if(ctx->part == NULL) { /* An error occurs */ @@ -491,7 +499,7 @@ vx_get(const size_t xyz[3], const uint64_t mcode, void* dst, void* context) } } - vx = partition_get_voxel(ctx->part, ivx); + vx = partition_cget_voxel(ctx->part, ivx); memcpy(dst, vx, NFLOATS_PER_VOXEL * sizeof(float)); } @@ -613,7 +621,7 @@ build_octrees if(res != RES_OK) goto error; exit: - if(ctx.part) pool_free_partition(pool, ctx.part); + if(ctx.part) partition_free(ctx.part); if(svx) SVX(device_ref_put(svx)); return res; error: @@ -630,7 +638,7 @@ create_octrees(struct rnatm* atm, const struct rnatm_create_args* args) /* Empirical constant that defines the number of voxel partitions to * pre-allocate per thread to avoid contension between the thread building the * octrees from the partitions and the threads that fill these partitions */ - const size_t NPARTITIONS_PER_THREAD = 1024; + const size_t NPARTITIONS_PER_THREAD = 64; struct pool_create_args pool_args = POOL_CREATE_ARGS_DEFAULT; struct pool* pool = NULL; @@ -638,8 +646,16 @@ create_octrees(struct rnatm* atm, const struct rnatm_create_args* args) ATOMIC res = RES_OK; ASSERT(atm); +/* grid_max_def = MMAX(MMAX( + atm->grid_definition[0], atm->grid_definition[1]), atm->grid_definition[2]); + + part_def = MMAX(grid_max_def / 128, 4); + npart_per_thread = part_def * part_def * part_def * 8; + nparts_per_thread = */ + /* Create the vortex partition pool */ - pool_args.npartitions = NPARTITIONS_PER_THREAD * atm->nthreads; + pool_args.npreallocated_partitions = NPARTITIONS_PER_THREAD * atm->nthreads; + pool_args.npartitions = pool_args.npreallocated_partitions * 64; pool_args.partition_definition = 8; pool_args.allocator = atm->allocator; res = pool_create(&pool_args, &pool); diff --git a/src/rnatm_voxel_partition.c b/src/rnatm_voxel_partition.c @@ -27,43 +27,47 @@ #include <rsys/mutex.h> #include <rsys/ref_count.h> -struct partition { - size_t definition; /* #voxels along the 3 dimensions */ +struct tile { + struct list_node node; + float voxels[1]; /* Flexible array member */ +}; +struct partition { struct list_node node; size_t id; /* Unique identifier of the partition */ - - /* List of voxels sorted according to the morton code of the voxel coords */ - float* voxels; - size_t nvoxels; - - struct mem_allocator* allocator; - ref_T ref; + struct tile* tile; /* Set of voxels */ + struct pool* pool; }; struct pool { - /* Linked list of pre-allocated partitions */ - struct list_node parts_free; + /* Allocated partitions and tiles */ + struct partition* parts; + char* tiles; + size_t tile_sz; /* Size in bytes of a tile */ + struct list_node parts_free; /* List of free partitions */ /* List of committed partition sorted in ascending order wrt partition id */ struct list_node parts_commit; + struct list_node tiles_free; /* List of available tiles of voxels */ + + /* Dummy voxel returned by a partition when no voxel is reserved for it */ + float empty_voxel[NFLOATS_PER_VOXEL]; + struct mutex* mutex; struct cond* cond_new; struct cond* cond_fetch; + struct cond* cond_tile; size_t next_part_id; /* Indentifier of the next partition */ size_t partition_definition; /* #voxels along the 3 axis */ + size_t partition_nvoxels; /* Overall number of voxels in a partition */ struct mem_allocator* allocator; ATOMIC error; /* Is the pool not valid? */ ref_T ref; }; -static INLINE void -partition_ref_put - (struct partition* partition); - /******************************************************************************* * Helper functions ******************************************************************************/ @@ -72,6 +76,7 @@ check_pool_create_args(const struct pool_create_args* args) { if(!args || !args->npartitions + || args->npartitions < args->npreallocated_partitions || !IS_POW2(args->partition_definition)) { return RES_BAD_ARG; } @@ -79,13 +84,105 @@ check_pool_create_args(const struct pool_create_args* args) return RES_OK; } +static INLINE void +tile_init(struct tile* tile) +{ + ASSERT(tile); + list_init(&tile->node); +} + +static INLINE void +tile_release(struct tile* tile) +{ + ASSERT(tile && is_list_empty(&tile->node)); + (void)tile; +} + +static void +partition_init + (struct pool* pool, + struct partition* partition) +{ + ASSERT(pool && partition); + list_init(&partition->node); + partition->id = SIZE_MAX; + partition->tile = NULL; + partition->pool = pool; +} + static void -release_partition(ref_T* ref) +partition_release(struct partition* partition) { - struct partition* partition = CONTAINER_OF(ref, struct partition, ref); ASSERT(partition && is_list_empty(&partition->node)); - if(partition->voxels) MEM_RM(partition->allocator, partition->voxels); - MEM_RM(partition->allocator, partition); + if(partition->tile) { + ASSERT(is_list_empty(&partition->tile->node)); + list_add(&partition->pool->tiles_free, &partition->tile->node); + } +} + +static struct partition* +get_free_partition(struct pool* pool) +{ + struct list_node* node = NULL; + struct partition* partition = NULL; + ASSERT(pool); + + mutex_lock(pool->mutex); + + /* Waits for a free partition */ + while(is_list_empty(&pool->parts_free) && !pool->error) { + cond_wait(pool->cond_new, pool->mutex); + } + + if(pool->error) { + /* An error occurs */ + partition = NULL; + mutex_unlock(pool->mutex); + + } else { + /* Retrieve the next available partition */ + node = list_head(&pool->parts_free); + list_del(node); + mutex_unlock(pool->mutex); + + partition = CONTAINER_OF(node, struct partition, node); + partition->id = SIZE_MAX; + } + return partition; +} + +static res_T +reserve_partition_voxels(struct partition* partition) +{ + struct pool* pool = NULL; + res_T res = RES_OK; + ASSERT(partition && !partition->tile); + + pool = partition->pool; + + mutex_lock(partition->pool->mutex); + + /* Waits for a free tile */ + while(is_list_empty(&pool->tiles_free) && !pool->error) { + cond_wait(pool->cond_tile, pool->mutex); + } + + if(pool->error) { + /* An error occurs */ + mutex_unlock(pool->mutex); + res = RES_UNKNOWN_ERR; + + } else { + struct list_node* node = NULL; + + /* Get an available voxel tile */ + node = list_head(&pool->tiles_free); + list_del(node); + mutex_unlock(pool->mutex); + + partition->tile = CONTAINER_OF(node, struct tile, node); + } + return res; } static void @@ -99,80 +196,101 @@ release_pool(ref_T* ref) if(pool->mutex) mutex_destroy(pool->mutex); if(pool->cond_new) cond_destroy(pool->cond_new); if(pool->cond_fetch) cond_destroy(pool->cond_fetch); + if(pool->cond_tile) cond_destroy(pool->cond_tile); LIST_FOR_EACH_SAFE(node, tmp_node, &pool->parts_free) { struct partition* partition = CONTAINER_OF(node, struct partition, node); list_del(node); - partition_ref_put(partition); + partition_release(partition); } LIST_FOR_EACH_SAFE(node, tmp_node, &pool->parts_commit) { struct partition* partition = CONTAINER_OF(node, struct partition, node); list_del(node); - partition_ref_put(partition); + partition_release(partition); + } + + LIST_FOR_EACH_SAFE(node, tmp_node, &pool->tiles_free) { + struct tile* tile = CONTAINER_OF(node, struct tile, node); + list_del(node); + tile_release(tile); } ASSERT(is_list_empty(&pool->parts_free)); ASSERT(is_list_empty(&pool->parts_commit)); + ASSERT(is_list_empty(&pool->tiles_free)); + + MEM_RM(pool->allocator, pool->parts); + MEM_RM(pool->allocator, pool->tiles); MEM_RM(pool->allocator, pool); } /******************************************************************************* * Partition of voxels ******************************************************************************/ -static res_T -partition_create - (const struct pool_create_args* args, - struct partition** out_partition) +void +partition_free(struct partition* partition) { - struct partition* partition = NULL; - struct mem_allocator* allocator = NULL; - size_t partsz = 0; - res_T res = RES_OK; - ASSERT(check_pool_create_args(args) == RES_OK && out_partition); + struct pool* pool = NULL; + int free_tile = 0; + ASSERT(partition); - allocator = args->allocator ? args->allocator : &mem_default_allocator; - partition = MEM_CALLOC(allocator, 1, sizeof(*partition)); - if(!partition) { - res = RES_MEM_ERR; - goto error; + pool = partition->pool; + + mutex_lock(pool->mutex); + list_move_tail(&partition->node, &pool->parts_free); /* Free the partition */ + if(partition->tile) { /* Free the reserved tile */ + list_move_tail(&partition->tile->node, &pool->tiles_free); + partition->tile = NULL; + free_tile = 1; } - partition->allocator = allocator; - ref_init(&partition->ref); - list_init(&partition->node); - partition->id = SIZE_MAX; - partition->definition = args->partition_definition; - partition->nvoxels = - partition->definition - * partition->definition - * partition->definition; - - /* Allocate the partition voxels. Align the voxels on the size of a cache - * line (i.e. 64 bytes) */ - partsz = partition->nvoxels * NFLOATS_PER_VOXEL * sizeof(float); - partition->voxels = MEM_ALLOC_ALIGNED(partition->allocator, partsz, 64); - if(!partition->voxels) { res = RES_MEM_ERR; goto error; } + mutex_unlock(pool->mutex); -exit: - *out_partition = partition; - return res; -error: - if(partition) partition_ref_put(partition); - goto exit; + /* Notify a thread waiting for a free partition that we just registered one */ + cond_signal(pool->cond_new); + + if(free_tile) { + /* Notify a partition waiting for a free tile that we just registered one */ + cond_signal(pool->cond_tile); + } } -static INLINE void -partition_ref_get(struct partition* partition) +void +partition_empty(struct partition* partition) { - ASSERT(partition); - ref_get(&partition->ref); + ASSERT(partition && partition->tile); + + mutex_lock(partition->pool->mutex); + list_move_tail(&partition->tile->node, &partition->pool->tiles_free); + partition->tile = NULL; + mutex_unlock(partition->pool->mutex); + + /* Notify a partition waiting for a free tile that we just registered one */ + cond_signal(partition->pool->cond_tile); } void -partition_ref_put(struct partition* partition) +partition_commit(struct partition* partition) { + struct list_node* node = NULL; + struct pool* pool = NULL; ASSERT(partition); - ref_put(&partition->ref, release_partition); + + pool = partition->pool; + + /* Committed partitions are sorted in ascending order of their id. We are + * therefore looking for the partition whose id is less than the id of the + * partition to be committed in order to add the latter in the right place */ + mutex_lock(pool->mutex); + LIST_FOR_EACH_REVERSE(node, &pool->parts_commit) { + struct partition* partition2 = CONTAINER_OF(node, struct partition, node); + if(partition2->id < partition->id) break; + } + list_add(node, &partition->node); + mutex_unlock(pool->mutex); + + /* Notify a thread waiting for a valid partition that we just registered one */ + cond_signal(pool->cond_fetch); } size_t @@ -186,14 +304,25 @@ size_t partition_get_definition(const struct partition* partition) { ASSERT(partition); - return partition->definition; + return partition->pool->partition_definition; } float* partition_get_voxel(struct partition* part, const size_t ivoxel) { - ASSERT(part && ivoxel < part->nvoxels); - return part->voxels + (ivoxel*NFLOATS_PER_VOXEL); + ASSERT(part && ivoxel < part->pool->partition_nvoxels && part->tile != NULL); + return part->tile->voxels + (ivoxel*NFLOATS_PER_VOXEL); +} + +const float* +partition_cget_voxel(struct partition* part, const size_t ivoxel) +{ + ASSERT(part && ivoxel < part->pool->partition_nvoxels); + if(part->tile == NULL) { + return part->pool->empty_voxel; + } else { + return part->tile->voxels + (ivoxel*NFLOATS_PER_VOXEL); + } } void @@ -202,7 +331,9 @@ partition_clear_voxels(struct partition* partition) size_t ivoxel = 0; ASSERT(partition); - FOR_EACH(ivoxel, 0, partition->nvoxels) { + if(partition->tile == NULL) return; /* Nothing to do */ + + FOR_EACH(ivoxel, 0, partition->pool->partition_nvoxels) { float* voxel = partition_get_voxel(partition, ivoxel); voxel_clear(voxel); } @@ -219,6 +350,9 @@ pool_create struct mem_allocator* allocator = NULL; struct pool* pool = NULL; size_t ipartition = 0; + size_t nvoxels = 0; + size_t tile_sz = 0; + res_T res = RES_OK; ASSERT(check_pool_create_args(args) == RES_OK && out_pool); @@ -230,9 +364,15 @@ pool_create } pool->allocator = allocator; pool->partition_definition = args->partition_definition; + pool->partition_nvoxels = + pool->partition_definition + * pool->partition_definition + * pool->partition_definition; ref_init(&pool->ref); list_init(&pool->parts_free); list_init(&pool->parts_commit); + list_init(&pool->tiles_free); + voxel_clear(pool->empty_voxel); /* Create the mutex and condition variables used to synchronize threads */ pool->mutex = mutex_create(); @@ -241,17 +381,37 @@ pool_create if(!pool->cond_new) { res = RES_UNKNOWN_ERR; goto error; } pool->cond_fetch = cond_create(); if(!pool->cond_fetch) { res = RES_UNKNOWN_ERR; goto error; } - - /* Pre-allocate the partitions */ + pool->cond_tile = cond_create(); + if(!pool->cond_tile) { res = RES_UNKNOWN_ERR; goto error; } + + /* Allocate the partitions */ + pool->parts = MEM_CALLOC(allocator, args->npartitions, sizeof(*pool->parts)); + if(!pool->parts) { res = RES_MEM_ERR; goto error; } + + /* Allocate the tiles */ + nvoxels = pool->partition_nvoxels; + tile_sz = + sizeof(struct tile) + - sizeof(float)/*Dummy member*/ + + sizeof(float[NFLOATS_PER_VOXEL]) * nvoxels; + tile_sz = ALIGN_SIZE(tile_sz, ALIGNOF(struct tile)); + pool->tiles = MEM_CALLOC(allocator, args->npreallocated_partitions, tile_sz); + if(!pool->tiles) { res = RES_MEM_ERR; goto error; } + + /* Setup the free partitions */ FOR_EACH(ipartition, 0, args->npartitions) { - struct partition* partition = NULL; - - res = partition_create(args, &partition); - if(res != RES_OK) goto error; - + struct partition* partition = pool->parts + ipartition; + partition_init(pool, partition); list_add(&pool->parts_free, &partition->node); } + /* Setup the free tiles */ + FOR_EACH(ipartition, 0, args->npreallocated_partitions) { + struct tile* tile = (struct tile*)(pool->tiles + ipartition*tile_sz); + tile_init(tile); + list_add(&pool->tiles_free, &tile->node); + } + exit: *out_pool = pool; return res; @@ -284,70 +444,23 @@ pool_get_partition_definition(const struct pool* pool) struct partition* pool_next_partition(struct pool* pool) { - struct list_node* node = NULL; - struct partition* partition = NULL; + struct partition* partition; + res_T res = RES_OK; ASSERT(pool); - mutex_lock(pool->mutex); - - /* Waits for a free partition */ - while(is_list_empty(&pool->parts_free) && !pool->error) { - cond_wait(pool->cond_new, pool->mutex); - } - - if(pool->error) { - /* An error occurs */ - partition = NULL; - mutex_unlock(pool->mutex); - - } else { - size_t ipartition; - - /* Retrieve the next available partition */ - node = list_head(&pool->parts_free); - list_del(node); - ipartition = pool->next_part_id++; - mutex_unlock(pool->mutex); - - partition = CONTAINER_OF(node, struct partition, node); - partition->id = ipartition; + partition = get_free_partition(pool); + res = reserve_partition_voxels(partition); + if(res != RES_OK) { + partition_free(partition); + return NULL; } - return partition; -} - -void -pool_free_partition(struct pool* pool, struct partition* partition) -{ - ASSERT(pool && partition); - - /* Register the partition as a free partition */ - mutex_lock(pool->mutex); - list_move_tail(&partition->node, &pool->parts_free); - mutex_unlock(pool->mutex); - - /* Notify a thread waiting for a free partition that we just registered one */ - cond_signal(pool->cond_new); -} - -void -pool_commit_partition(struct pool* pool, struct partition* partition) -{ - struct list_node* node = NULL; - ASSERT(pool && partition); - /* Committed partitions are sorted in ascending order of their id. We are - * therefore looking for the partition whose id is less than the id of the - * partition to be committed in order to add the latter in the right place */ mutex_lock(pool->mutex); - LIST_FOR_EACH_REVERSE(node, &pool->parts_commit) { - struct partition* partition2 = CONTAINER_OF(node, struct partition, node); - if(partition2->id < partition->id) break; - } - list_add(node, &partition->node); + partition->id = pool->next_part_id; + pool->next_part_id += 1; mutex_unlock(pool->mutex); - /* Notify a thread waiting for a valid partition that we just registered one */ - cond_signal(pool->cond_fetch); + return partition; } struct partition* @@ -403,4 +516,5 @@ pool_invalidate(struct pool* pool) /* Wakes up all the waiting threads */ cond_broadcast(pool->cond_new); cond_broadcast(pool->cond_fetch); + cond_broadcast(pool->cond_tile); } diff --git a/src/rnatm_voxel_partition.h b/src/rnatm_voxel_partition.h @@ -27,12 +27,16 @@ #include <rsys/rsys.h> struct pool_create_args { - size_t npartitions; /* Number of partitions to preallocate */ size_t partition_definition; /* #voxels along XYZ. Must be a power of 2 */ + size_t npartitions; /* Overall number of partitions managed by the pool */ + + /* Number of pre-allocated partitions must be <= npartitions */ + size_t npreallocated_partitions; + struct mem_allocator* allocator; /* NULL <=> default allocator */ }; -#define POOL_CREATE_ARGS_DEFAULT__ {0, 32, NULL} +#define POOL_CREATE_ARGS_DEFAULT__ {0, 32, 32, NULL} static const struct pool_create_args POOL_CREATE_ARGS_DEFAULT = POOL_CREATE_ARGS_DEFAULT__; @@ -41,6 +45,15 @@ static const struct pool_create_args POOL_CREATE_ARGS_DEFAULT = ******************************************************************************/ struct partition; +extern LOCAL_SYM void +partition_free + (struct partition* partition); + +/* Commit the partitition, i.e. it can be fetched from the pool */ +extern LOCAL_SYM void +partition_commit + (struct partition* partition); + extern LOCAL_SYM size_t partition_get_id (const struct partition* partition); @@ -49,11 +62,25 @@ extern LOCAL_SYM size_t partition_get_definition (const struct partition* partition); +/* Empty the partition, that is, its voxels are freed. Therefore, the + * partition_get_voxel function can no longer be called; you have to use the + * partition_cget_voxel function instead */ +extern LOCAL_SYM void +partition_empty + (struct partition* partition); + extern LOCAL_SYM float* partition_get_voxel (struct partition* partition, const size_t ivoxel); +/* Returns a voxel even if the partition is empty. In the latter case, it + * always returns an empty voxel */ +extern LOCAL_SYM const float* +partition_cget_voxel + (struct partition* partition, + const size_t ivoxel); + extern LOCAL_SYM void partition_clear_voxels (struct partition* partition); @@ -87,18 +114,6 @@ extern LOCAL_SYM struct partition* pool_next_partition (struct pool* pool); -/* Saves the partition in the pool as a free partition */ -extern LOCAL_SYM void -pool_free_partition - (struct pool* pool, - struct partition* partition); - -/* Save partition as a valid recoverable partition, i.e. it can be fetched */ -extern LOCAL_SYM void -pool_commit_partition - (struct pool* pool, - struct partition* partition); - /* Returns the partition with the identifier 'ipartition'. Waits for the * partition to be available. Returns NULL if an error occurs */ extern LOCAL_SYM struct partition*