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:
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);