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:
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*