star-3d

Surface structuring for efficient 3D geometric queries
git clone git://git.meso-star.fr/star-3d.git
Log | Files | Refs | README | LICENSE

commit 5713601a714672252a7e9733d98f7580e2a0c0f9
parent 44a91f2abfd7be202bd1254298c2c5474029a270
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Tue, 17 Mar 2015 20:59:09 +0100

Begin to add thread safety to the s3d API

Diffstat:
Msrc/s3d.h | 4++++
Msrc/s3d_scene.c | 134++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/s3d_scene_c.h | 14++++++++++++--
Msrc/s3d_shape.c | 35++++++++++++++++++++++-------------
Msrc/s3d_shape_c.h | 4++--
Msrc/test_s3d_cbox.h | 10+++++-----
Msrc/test_s3d_trace_ray.c | 2++
7 files changed, 149 insertions(+), 54 deletions(-)

diff --git a/src/s3d.h b/src/s3d.h @@ -178,6 +178,10 @@ S3D_API res_T s3d_scene_clear (struct s3d_scene* scn); +S3D_API res_T +s3d_scene_pull + (struct s3d_scene* scn); + /* Trace a ray into the scene and return the closest intersection. The ray is * defined by `origin' + t*`direction' = 0 with t in [`range[0]', `range[1]'). * Note that if range is degenerated (i.e. `range[0]' >= `range[1]') then the diff --git a/src/s3d_scene.c b/src/s3d_scene.c @@ -37,6 +37,7 @@ #include <rsys/float3.h> #include <rsys/mem_allocator.h> +#include <rsys/mutex.h> #include <embree2/rtcore_ray.h> @@ -46,10 +47,10 @@ static void delete_rtc_geometry(struct s3d_scene* scn, struct s3d_shape* shape) { - ASSERT(scn && shape && shape->rtc_geom != INVALID_RTC_GEOMETRY); + ASSERT(scn && shape && shape->rtc_geom != RTC_INVALID_GEOMETRY_ID); rtcDeleteGeometry(scn->rtc_scn, shape->rtc_geom); darray_geom2shape_data_get(&scn->geom2shape)[shape->rtc_geom] = NULL; - shape->rtc_geom = INVALID_RTC_GEOMETRY; + shape->rtc_geom = RTC_INVALID_GEOMETRY_ID; } static res_T @@ -70,13 +71,14 @@ scene_setup(struct s3d_scene* scn) /*ASSERT(IS_ALIGNED(ids, 16));*/ /* The Embree geometry is no more valid */ - if(shape->data.mesh.resize_mask && shape->rtc_geom!=INVALID_RTC_GEOMETRY) { + if(shape->data.mesh.resize_mask + && shape->rtc_geom != RTC_INVALID_GEOMETRY_ID) { rtcDeleteGeometry(scn->rtc_scn, shape->rtc_geom); - shape->rtc_geom = INVALID_RTC_GEOMETRY; + shape->rtc_geom = RTC_INVALID_GEOMETRY_ID; shape->data.mesh.resize_mask = 0; } - if(shape->rtc_geom != INVALID_RTC_GEOMETRY) { + if(shape->rtc_geom != RTC_INVALID_GEOMETRY_ID) { /* Update the Embree geometry if required */ if(shape->data.mesh.update_mask & MESH_INDEX_BUFFER) rtcUpdateBuffer(scn->rtc_scn, shape->rtc_geom, RTC_INDEX_BUFFER); @@ -99,21 +101,36 @@ scene_setup(struct s3d_scene* scn) darray_geom2shape_data_get(&scn->geom2shape)[shape->rtc_geom] = shape; } } - /* Commit the scene updates */ - rtcCommit(scn->rtc_scn); + exit: + rtcCommit(scn->rtc_scn); return res; error: LIST_FOR_EACH(node, &scn->shapes) { struct s3d_shape* shape = CONTAINER_OF (node, struct s3d_shape, scene_attachment); - if(shape->rtc_geom != INVALID_RTC_GEOMETRY) + if(shape->rtc_geom != RTC_INVALID_GEOMETRY_ID) delete_rtc_geometry(scn, shape); } rtcCommit(scn->rtc_scn); goto exit; } +static INLINE void +scene_remove_shape_unsafe(struct s3d_scene* scn, struct s3d_shape* shape) +{ + ASSERT(shape->scn == scn); + mutex_rw_wlock(shape->lock); + if(is_list_empty(&shape->scene_attachment)) /* No more attached */ + return; + if(shape->rtc_geom != RTC_INVALID_GEOMETRY_ID) + delete_rtc_geometry(scn, shape); + list_del(&shape->scene_attachment); + shape->scn = NULL; + mutex_rw_unlock(shape->lock); + S3D(shape_ref_put(shape)); +} + static void scene_release(ref_T* ref) { @@ -125,6 +142,8 @@ scene_release(ref_T* ref) dev = scn->dev; if(scn->rtc_scn) rtcDeleteScene(scn->rtc_scn); + if(scn->lock) + mutex_destroy(scn->lock); darray_geom2shape_release(&scn->geom2shape); MEM_FREE(dev->allocator, scn); S3D(device_ref_put(dev)); @@ -154,13 +173,22 @@ s3d_scene_create(struct s3d_device* dev, struct s3d_scene** out_scn) ref_init(&scn->ref); S3D(device_ref_get(dev)); scn->dev = dev; + scn->is_outdated = 0; + scn->status = SCENE_READY; scn->rtc_scn = rtcNewScene - (RTC_SCENE_STATIC | RTC_SCENE_INCOHERENT, + (RTC_SCENE_DYNAMIC | RTC_SCENE_INCOHERENT, RTC_INTERSECT1 | RTC_INTERSECT4); if(!scn->rtc_scn) { res = RES_MEM_ERR; goto error; } + scn->lock = mutex_create(); + if(!scn->lock) { + res = RES_MEM_ERR; + goto error; + } + /* Commit empty scene => the scene can be ray traced whithout any pull */ + rtcCommit(scn->rtc_scn); exit: if(out_scn) *out_scn = scn; @@ -192,14 +220,25 @@ s3d_scene_ref_put(struct s3d_scene* scn) res_T s3d_scene_attach_shape(struct s3d_scene* scn, struct s3d_shape* shape) { - if(!scn || !shape || !is_list_empty(&shape->scene_attachment)) + if(!scn || !shape) return RES_BAD_ARG; + + mutex_lock(scn->lock); + + if(!is_list_empty(&shape->scene_attachment)) { + mutex_unlock(scn->lock); + return RES_BAD_ARG; + } + ASSERT(shape->scn == NULL); - ASSERT(shape->rtc_geom == INVALID_RTC_GEOMETRY); - S3D(shape_ref_get(shape)); + ASSERT(shape->rtc_geom == RTC_INVALID_GEOMETRY_ID); list_add_tail(&scn->shapes, &shape->scene_attachment); shape->scn = scn; - scn->is_updated = 1; + scn->is_outdated = 1; + + mutex_unlock(scn->lock); + + S3D(shape_ref_get(shape)); return RES_OK; } @@ -210,15 +249,51 @@ s3d_scene_clear(struct s3d_scene* scn) if(!scn) return RES_BAD_ARG; + mutex_lock(scn->lock); LIST_FOR_EACH_SAFE(node, tmp, &scn->shapes) { struct s3d_shape* shape = CONTAINER_OF (node, struct s3d_shape, scene_attachment); - S3D(shape_detach(shape)); + scene_remove_shape_unsafe(scn, shape); } + mutex_unlock(scn->lock); return RES_OK; } res_T +s3d_scene_pull(struct s3d_scene* scn) +{ + res_T res = RES_OK; + int status; + if(!scn) { + res = RES_BAD_ARG; + goto error; + } + + if(!scn->is_outdated) + goto exit; + + if(ATOMIC_CAS(&scn->status, SCENE_PULL, SCENE_READY) == SCENE_RAY_TRACE) { + res = RES_BAD_ARG; /* TODO return a RES_BAD_OP instead */ + goto error; + } else { + mutex_lock(scn->lock); + if(scn->is_outdated) { + res = scene_setup(scn); + scn->is_outdated = res != RES_OK; + } + mutex_unlock(scn->lock); + ATOMIC_SET(&scn->status, SCENE_READY); + if(res != RES_OK) + goto error; + } + +exit: + return res; +error: + goto exit; +} + +res_T s3d_scene_trace_ray (struct s3d_scene* scn, const float org[3], @@ -228,18 +303,13 @@ s3d_scene_trace_ray { struct RTCRay ray; res_T res = RES_OK; - if(!scn || !org || !dir || !range || !hit) { - res = RES_BAD_ARG; - goto error; - } - if(!f3_is_normalized(dir) || range[0] < 0.f || range[0] > range[1]) { - res = RES_BAD_ARG; - goto error; - } - if(scn->is_updated) { - scene_setup(scn); - scn->is_updated = 0; - } + if(!scn || !org || !dir || !range || !hit) + return RES_BAD_ARG; + if(!f3_is_normalized(dir) || range[0] < 0.f || range[0] > range[1]) + return RES_BAD_ARG; + + if(ATOMIC_CAS(&scn->status, SCENE_RAY_TRACE, SCENE_READY) == SCENE_PULL) + return RES_BAD_ARG; /* return a RES_BAD_OP instead */ f3_set(ray.org, org); f3_set(ray.dir, dir); @@ -250,7 +320,9 @@ s3d_scene_trace_ray ray.instID = RTC_INVALID_GEOMETRY_ID; ray.mask = 0xFFFFFFFF; ray.time = 0.f; + rtcIntersect(scn->rtc_scn, ray); + ATOMIC_SET(&scn->status, SCENE_READY); if(ray.geomID == RTC_INVALID_GEOMETRY_ID) { hit->shape = NULL; @@ -277,11 +349,9 @@ error: void scene_remove_shape(struct s3d_scene* scn, struct s3d_shape* shape) { - ASSERT(shape->scn == scn); - if(shape->rtc_geom != INVALID_RTC_GEOMETRY) - delete_rtc_geometry(scn, shape); - list_del(&shape->scene_attachment); - shape->scn = NULL; - S3D(shape_ref_put(shape)); + ASSERT(scn); + mutex_lock(scn->lock); + scene_remove_shape_unsafe(scn, shape); + mutex_unlock(scn->lock); } diff --git a/src/s3d_scene_c.h b/src/s3d_scene_c.h @@ -43,12 +43,22 @@ #define DARRAY_DATA struct s3d_shape* #include <rsys/dynamic_array.h> +enum scene_status { + SCENE_PULL, /* The scene is pulling its updates */ + SCENE_RAY_TRACE, /* The scene is ray-traced */ + SCENE_READY /* The scene can accept any operation */ +}; + struct s3d_scene { struct list_node shapes; /* List of attached shapes */ struct darray_geom2shape geom2shape; - struct s3d_device* dev; RTCScene rtc_scn; - char is_updated; /* Flag defining if the scene description was updated */ + char is_outdated; /* Flag defining if the scene description was updated */ + int status; + + struct mutex* lock; + + struct s3d_device* dev; ref_T ref; }; diff --git a/src/s3d_shape.c b/src/s3d_shape.c @@ -53,14 +53,6 @@ get_s3d_type_dimension(const enum s3d_type type) return 0; } -static FINLINE void -shape_delete_rtc_geometry(struct s3d_shape* shape) -{ - ASSERT(shape && shape->rtc_geom != INVALID_RTC_GEOMETRY && shape->scn); - rtcDeleteGeometry(shape->scn->rtc_scn, shape->rtc_geom); - shape->rtc_geom = INVALID_RTC_GEOMETRY; -} - static void mesh_init(struct mem_allocator* allocator, struct mesh* mesh) { @@ -258,12 +250,19 @@ shape_release(ref_T* ref) ASSERT(ref); shape = CONTAINER_OF(ref, struct s3d_shape, ref); dev = shape->dev; + + /* It is unacessary to protect the read of the scene_attachment, scn and + * rtc_geom fields since the shape should not be shared when it is released */ + /* The shape should not be attached */ ASSERT(is_list_empty(&shape->scene_attachment)); ASSERT(shape->scn == NULL); /* The rtc_geom should be invalid */ - ASSERT(shape->rtc_geom == INVALID_RTC_GEOMETRY); + ASSERT(shape->rtc_geom == RTC_INVALID_GEOMETRY_ID); + shape_release_data(shape); + if(shape->lock) + mutex_rw_destroy(shape->lock); MEM_FREE(dev->allocator, shape); S3D(device_ref_put(dev)); } @@ -291,11 +290,16 @@ s3d_shape_create_mesh list_init(&shape->scene_attachment); mesh_init(dev->allocator, &shape->data.mesh); shape->type = SHAPE_MESH; - shape->rtc_geom = INVALID_RTC_GEOMETRY; + shape->rtc_geom = RTC_INVALID_GEOMETRY_ID; shape->scn = NULL; S3D(device_ref_get(dev)); shape->dev = dev; ref_init(&shape->ref); + shape->lock = mutex_rw_create(); + if(!shape->lock) { + res = RES_MEM_ERR; + goto error; + } exit: if(out_shape) *out_shape = shape; @@ -327,10 +331,13 @@ s3d_shape_ref_put(struct s3d_shape* shape) res_T s3d_shape_detach(struct s3d_shape* shape) { + char is_not_attached; if(!shape) return RES_BAD_ARG; - if(is_list_empty(&shape->scene_attachment)) /* The shape is not attached */ + mutex_rw_rlock(shape->lock); + is_not_attached = is_list_empty(&shape->scene_attachment); + mutex_rw_unlock(shape->lock); + if(is_not_attached) return RES_OK; - ASSERT(shape->scn); scene_remove_shape(shape->scn, shape); return RES_OK; } @@ -404,8 +411,10 @@ s3d_shape_mesh_setup_indexed_vertices mesh_setup_attribs(&shape->data.mesh, nverts, attribs + iattr, data); } } + mutex_rw_rlock(shape->lock); if(shape->scn) /* Notify the shape update */ - shape->scn->is_updated = 1; + shape->scn->is_outdated = 1; + mutex_rw_unlock(shape->lock); exit: return res; diff --git a/src/s3d_shape_c.h b/src/s3d_shape_c.h @@ -36,6 +36,7 @@ #include <rsys/dynamic_array_u32.h> #include <rsys/dynamic_array_float.h> #include <rsys/list.h> +#include <rsys/mutex.h> #include <rsys/ref_count.h> #include <embree2/rtcore.h> @@ -64,13 +65,12 @@ struct mesh { /* Triangular mesh */ int resize_mask; /* Define which shape buffers were [re]allocateod */ }; -#define INVALID_RTC_GEOMETRY UINT_MAX - struct s3d_shape { struct list_node scene_attachment; enum shape_type type; unsigned rtc_geom; /* Embree geometry id */ struct s3d_scene* scn; /* The scene on which the shape is attached */ + struct mutex_rw* lock; union { struct s3d_scene* scene; diff --git a/src/test_s3d_cbox.h b/src/test_s3d_cbox.h @@ -69,11 +69,11 @@ const unsigned cbox_nverts = (unsigned)(sizeof(cbox_verts)/(sizeof(float[3]))); const uint32_t cbox_ids[] = { /* Box */ - 0, 1, 2, 2, 3, 0, - 4, 5, 6, 6, 7, 4, - 1, 2, 6, 6, 5, 1, - 0, 3, 7, 7, 4, 0, - 2, 3, 7, 7, 6, 2, + 0, 1, 2, 2, 3, 0, /* Bottom */ + 4, 5, 6, 6, 7, 4, /* Top */ + 1, 2, 6, 6, 5, 1, /* Left */ + 0, 3, 7, 7, 4, 0, /* Right */ + 2, 3, 7, 7, 6, 2, /* Back */ /* Short block */ 12, 13, 14, 14, 15, 12, 9, 10, 14, 14, 13, 9, diff --git a/src/test_s3d_trace_ray.c b/src/test_s3d_trace_ray.c @@ -150,6 +150,8 @@ main(int argc, char** argv) CHECK(s3d_scene_trace_ray(scn, NULL, dir, range, &hit), RES_BAD_ARG); CHECK(s3d_scene_trace_ray(NULL, org, dir, range, &hit), RES_BAD_ARG); CHECK(s3d_scene_trace_ray(scn, org, dir, range, &hit), RES_OK); + CHECK(s3d_scene_pull(scn), RES_OK); + CHECK(s3d_scene_trace_ray(scn, org, dir, range, &hit), RES_OK); f3(dir, 1.f, 1.f, 1.f); CHECK(s3d_scene_trace_ray(scn, org, dir, range, &hit), RES_BAD_ARG);