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 ff819559a704106ada6e09ffe54ff24cf45f53da
parent 5713601a714672252a7e9733d98f7580e2a0c0f9
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 18 Mar 2015 12:04:02 +0100

Pursue to add thread safety to the s3d API

Diffstat:
Msrc/s3d.h | 6++++++
Msrc/s3d_scene.c | 39++++++++++++++++++++++++++-------------
Msrc/s3d_scene_c.h | 3++-
Msrc/s3d_shape.c | 51+++++++++++++++++++++++++++++++++++++++------------
Msrc/s3d_shape_c.h | 7+++++++
5 files changed, 80 insertions(+), 26 deletions(-)

diff --git a/src/s3d.h b/src/s3d.h @@ -222,6 +222,12 @@ s3d_shape_enable (struct s3d_shape* shape, const char enable); +/* Define whether the shape is attached or not */ +S3D_API res_T +s3d_shape_is_attached + (struct s3d_shape* shape, + char* is_attached); + /* Remove the shape from the scene on which it is attached. No error is * reported if the shape is not attached to a scene. After its detachment, the * scene release its reference on the shape */ diff --git a/src/s3d_scene.c b/src/s3d_scene.c @@ -103,7 +103,6 @@ scene_setup(struct s3d_scene* scn) } exit: - rtcCommit(scn->rtc_scn); return res; error: LIST_FOR_EACH(node, &scn->shapes) { @@ -223,10 +222,14 @@ s3d_scene_attach_shape(struct s3d_scene* scn, struct s3d_shape* shape) if(!scn || !shape) return RES_BAD_ARG; + /* Prevent the scene_<clear|pull|remove_shape> oprations */ mutex_lock(scn->lock); + /* Prevent the shape_<attach|detach|setup> operations */ + mutex_rw_wlock(shape->lock); if(!is_list_empty(&shape->scene_attachment)) { mutex_unlock(scn->lock); + mutex_rw_unlock(shape->lock); return RES_BAD_ARG; } @@ -237,7 +240,10 @@ s3d_scene_attach_shape(struct s3d_scene* scn, struct s3d_shape* shape) scn->is_outdated = 1; mutex_unlock(scn->lock); + mutex_rw_unlock(shape->lock); + /* The shape should be own by the caller. Consequently, the shape cannot be + * deleted before the end of the attach process */ S3D(shape_ref_get(shape)); return RES_OK; } @@ -249,6 +255,7 @@ s3d_scene_clear(struct s3d_scene* scn) if(!scn) return RES_BAD_ARG; + /* Prevent the scene_<attach_shape|remove_shape|pull> operations */ mutex_lock(scn->lock); LIST_FOR_EACH_SAFE(node, tmp, &scn->shapes) { struct s3d_shape* shape = CONTAINER_OF @@ -263,25 +270,28 @@ res_T s3d_scene_pull(struct s3d_scene* scn) { res_T res = RES_OK; - int status; - if(!scn) { - res = RES_BAD_ARG; - goto error; - } + enum scene_status status; - if(!scn->is_outdated) - goto exit; + if(!scn) + return RES_BAD_ARG; - if(ATOMIC_CAS(&scn->status, SCENE_PULL, SCENE_READY) == SCENE_RAY_TRACE) { + status = ATOMIC_CAS(&scn->status, SCENE_PULL, SCENE_READY); + if(status == SCENE_RAY_TRACE || status == SCENE_COMMIT) { + /* The scene cannot be pulled while it is ray traced or updated */ res = RES_BAD_ARG; /* TODO return a RES_BAD_OP instead */ goto error; } else { + /* Prevent the scene_<attach/remove_shape|clear|pull> operations */ mutex_lock(scn->lock); if(scn->is_outdated) { res = scene_setup(scn); + rtcCommit(scn->rtc_scn); scn->is_outdated = res != RES_OK; } mutex_unlock(scn->lock); + + /* The status is necessaraly SCENE_PULL */ + ASSERT(scn->status == SCENE_PULL); ATOMIC_SET(&scn->status, SCENE_READY); if(res != RES_OK) goto error; @@ -308,8 +318,10 @@ s3d_scene_trace_ray 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 */ + if(ATOMIC_CAS(&scn->status, SCENE_RAY_TRACE, SCENE_READY) == SCENE_PULL) { + /* A scene cannot be ray-traced while it is pulled */ + return RES_BAD_ARG; /* TODO return a RES_BAD_OP instead */ + } f3_set(ray.org, org); f3_set(ray.dir, dir); @@ -322,10 +334,10 @@ s3d_scene_trace_ray ray.time = 0.f; rtcIntersect(scn->rtc_scn, ray); - ATOMIC_SET(&scn->status, SCENE_READY); + ATOMIC_CAS(&scn->status, SCENE_READY, SCENE_RAY_TRACE); if(ray.geomID == RTC_INVALID_GEOMETRY_ID) { - hit->shape = NULL; + *hit = S3D_HIT_NULL; } else { f3_set(hit->normal, ray.Ng); hit->uv[0] = ray.u; @@ -337,6 +349,7 @@ s3d_scene_trace_ray ASSERT(hit->shape != NULL && ray.geomID == hit->shape->rtc_geom); } + exit: return res; error: diff --git a/src/s3d_scene_c.h b/src/s3d_scene_c.h @@ -45,6 +45,7 @@ enum scene_status { SCENE_PULL, /* The scene is pulling its updates */ + SCENE_COMMIT, /* Some scene shapes are updating */ SCENE_RAY_TRACE, /* The scene is ray-traced */ SCENE_READY /* The scene can accept any operation */ }; @@ -54,7 +55,7 @@ struct s3d_scene { struct darray_geom2shape geom2shape; RTCScene rtc_scn; char is_outdated; /* Flag defining if the scene description was updated */ - int status; + enum scene_status status; struct mutex* lock; diff --git a/src/s3d_shape.c b/src/s3d_shape.c @@ -251,8 +251,9 @@ shape_release(ref_T* 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 */ + /* It is unacessary to prevent the concurrent read/write 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)); @@ -290,6 +291,7 @@ s3d_shape_create_mesh list_init(&shape->scene_attachment); mesh_init(dev->allocator, &shape->data.mesh); shape->type = SHAPE_MESH; + shape->status = SHAPE_STATUS_READY; shape->rtc_geom = RTC_INVALID_GEOMETRY_ID; shape->scn = NULL; S3D(device_ref_get(dev)); @@ -329,14 +331,25 @@ s3d_shape_ref_put(struct s3d_shape* shape) } res_T -s3d_shape_detach(struct s3d_shape* shape) +s3d_shape_is_attached(struct s3d_shape* shape, char* is_attached) { - char is_not_attached; - if(!shape) return RES_BAD_ARG; + if(!shape || !is_attached) + return RES_BAD_ARG; + /* Prevent the shape from its concurrent attachment/detachment. Anyway, + * several threads can concurrently query for its attachement status */ mutex_rw_rlock(shape->lock); - is_not_attached = is_list_empty(&shape->scene_attachment); + *is_attached = !is_list_empty(&shape->scene_attachment); + ASSERT(!*is_attached || shape->scn); mutex_rw_unlock(shape->lock); - if(is_not_attached) + return RES_OK; +} + +res_T +s3d_shape_detach(struct s3d_shape* shape) +{ + char is_attached; + if(!shape) return RES_BAD_ARG; + if(S3D(shape_is_attached(shape, &is_attached)), !is_attached) return RES_OK; scene_remove_shape(shape->scn, shape); return RES_OK; @@ -359,10 +372,22 @@ s3d_shape_mesh_setup_indexed_vertices char has_position = 0; res_T res = RES_OK; - if(!shape || shape->type != SHAPE_MESH || !ntris || !nverts || !attribs) { - res = RES_BAD_ARG; - goto error; + if(!shape || shape->type != SHAPE_MESH || !ntris || !nverts || !attribs) + return RES_BAD_ARG; + + /* Prevent the shape from its concurrent attachment/detachment & update */ + mutex_rw_wlock(shape->lock); + + if(!is_list_empty(&shape->scene_attachment)) { + if(ATOMIC_CAS(&shape->scn->status, SCENE_COMMIT, SCENE_READY)==SCENE_PULL) { + /* Is there any use case where a scene is pulling shapes while its + * associated data were updated ? Currently it seems that it reveals only + * an unexpected comportment */ + res = RES_BAD_ARG; /* FIXME return a RES_BAD_OP instead */ + goto error; + } } + /* Check indices description */ if(get_indices == S3D_KEEP) { const unsigned nids_prev = darray_u32_size_get(&shape->data.mesh.indices); @@ -411,12 +436,14 @@ 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_outdated = 1; - mutex_rw_unlock(shape->lock); exit: + if(!is_list_empty(&shape->scene_attachment)) /* Restore scene status */ + ATOMIC_CAS(&shape->scn->status, SCENE_READY, SCENE_COMMIT); + mutex_rw_unlock(shape->lock); return res; error: goto exit; diff --git a/src/s3d_shape_c.h b/src/s3d_shape_c.h @@ -48,6 +48,12 @@ enum mesh_buffer { MESH_VERTEX_BUFFER = BIT(1) }; +enum shape_status { + SHAPE_STATUS_COMMIT, /* Shape data are committed */ + SHAPE_STATUS_PULL, /* Shape data are pulled */ + SHAPE_STATUS_READY /* The shape is ready for any operation */ +}; + enum shape_type { SHAPE_MESH, SHAPE_SCENE, @@ -68,6 +74,7 @@ struct mesh { /* Triangular mesh */ struct s3d_shape { struct list_node scene_attachment; enum shape_type type; + enum shape_status status; unsigned rtc_geom; /* Embree geometry id */ struct s3d_scene* scn; /* The scene on which the shape is attached */ struct mutex_rw* lock;