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 6eb8e09b01b17b98d304e0565025afd718993552
parent ee8cf0567a598920f9710039864f7c936880d9f4
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Mon, 25 Jul 2016 09:20:30 +0200

Allow multiple scene sessions on the same scene

A scene can have several active sessions independently of the instancing
feature.

Diffstat:
Msrc/s3d_geometry.h | 11+++++++++++
Msrc/s3d_scene.c | 409+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/s3d_scene_c.h | 5+++--
Msrc/test_s3d_scene.c | 9++++++---
4 files changed, 254 insertions(+), 180 deletions(-)

diff --git a/src/s3d_geometry.h b/src/s3d_geometry.h @@ -44,11 +44,22 @@ enum geometry_type { GEOM_NONE = GEOM_TYPES_COUNT__ }; +enum embree_attrib { + EMBREE_ENABLE = BIT(0), + EMBREE_FILTER_FUNCTION = BIT(1), + EMBREE_INDICES = BIT(2), + EMBREE_TRANSFORM = BIT(4), + EMBREE_VERTICES = BIT(5) +}; + /* Backend geometry */ struct geometry { unsigned name; /* Client side identifier */ unsigned irtc; /* Embree identifier */ unsigned scene_prim_id_offset; /* Offset from local to scene prim_id */ + + int embree_outdated_mask; /* Combination of embree_attrib */ + char flip_surface; /* Is the geometry surface flipped? */ char is_enabled; /* Is the geometry enabled? */ diff --git a/src/s3d_scene.c b/src/s3d_scene.c @@ -42,14 +42,13 @@ #include <algorithm> -/* Flag used to define session enabled on instantiated scene */ -#define S3D_INSTANCE (BIT(sizeof(int)*8 - 1)) - struct ray_extended : public RTCRay { struct s3d_scene* scene; void* data; /* User defined data */ }; +#define INTERNAL_SESSION ((unsigned char)(-1)) + /******************************************************************************* * Helper functions ******************************************************************************/ @@ -146,49 +145,8 @@ filter_wrapper(void* user_ptr, RTCRay& ray) static res_T scene_sync (struct s3d_scene* scn, - const int mask);/* combination of s3d_session_flag & S3D_INSTANCE */ - -static INLINE void -scene_geometry_flush_enable_state - (struct s3d_scene* scn, - struct geometry* geom, /* Cached geometry */ - const struct s3d_shape* shape) /* New shape */ -{ - ASSERT(scn && geom && shape); - if(geom->is_enabled == shape->is_enabled) - return; - - geom->is_enabled = shape->is_enabled; - if(geom->is_enabled) { - rtcEnable(scn->rtc_scn, geom->irtc); - } else { - rtcDisable(scn->rtc_scn, geom->irtc); - } - scn->is_rtc_scn_outdated = 1; -} - -static INLINE void -scene_geometry_flush_filter_function - (struct s3d_scene* scn, - struct geometry* geom, /* Cached geometry */ - const struct s3d_shape* shape) -{ - ASSERT(scn && geom && shape && geom->irtc != RTC_INVALID_GEOMETRY_ID); - ASSERT(shape->type == GEOM_MESH); - - if(geom->data.mesh->filter.func == shape->data.mesh->filter.func - && geom->data.mesh->filter.data == shape->data.mesh->filter.data) - return; /* Up to date */ - - geom->data.mesh->filter = shape->data.mesh->filter; - if(!geom->data.mesh->filter.func) { - rtcSetIntersectionFilterFunction(scn->rtc_scn, geom->irtc, NULL); - } else { - rtcSetIntersectionFilterFunction(scn->rtc_scn, geom->irtc, filter_wrapper); - rtcSetUserData(scn->rtc_scn, geom->irtc, &geom->data.mesh->filter); - } - scn->is_rtc_scn_outdated = 1; -} + const int mask, /* Combination of s3d_session_flag */ + const int internal); /* Is the synchronized session internal? */ static res_T scene_register_embree_geometry(struct s3d_scene* scn, struct geometry* geom) @@ -210,8 +168,6 @@ scene_register_embree_geometry(struct s3d_scene* scn, struct geometry* geom) } if(geom->irtc == RTC_INVALID_GEOMETRY_ID) return RES_UNKNOWN_ERR; - - scn->is_rtc_scn_outdated = 1; } if(geom->irtc >= darray_geom_size_get(&scn->embree2geoms)) { @@ -222,52 +178,172 @@ scene_register_embree_geometry(struct s3d_scene* scn, struct geometry* geom) return res; } } - geometry_ref_get(geom); darray_geom_data_get(&scn->embree2geoms)[geom->irtc] = geom; return RES_OK; } static INLINE void -scene_geometry_flush_positions(struct s3d_scene* scn, struct geometry* geom) +scene_setup_embree_geometry_positions + (struct s3d_scene* scn, struct geometry* geom) { ASSERT(scn && geom && geom->type == GEOM_MESH); ASSERT(geom->irtc != RTC_INVALID_GEOMETRY_ID); rtcSetBuffer(scn->rtc_scn, geom->irtc, RTC_VERTEX_BUFFER, mesh_get_pos(geom->data.mesh), 0, sizeof(float[3])); rtcUpdateBuffer(scn->rtc_scn, geom->irtc, RTC_VERTEX_BUFFER); - scn->is_rtc_scn_outdated = 1; } static INLINE void -scene_geometry_flush_indices(struct s3d_scene* scn, struct geometry* geom) +scene_setup_embree_geometry_indices + (struct s3d_scene* scn, struct geometry* geom) { ASSERT(scn && geom && geom->type == GEOM_MESH); ASSERT(geom->irtc != RTC_INVALID_GEOMETRY_ID); rtcSetBuffer(scn->rtc_scn, geom->irtc, RTC_INDEX_BUFFER, mesh_get_ids(geom->data.mesh), 0, sizeof(uint32_t[3])); rtcUpdateBuffer(scn->rtc_scn, geom->irtc, RTC_INDEX_BUFFER); - scn->is_rtc_scn_outdated = 1; } -static void -scene_session_clear(struct s3d_scene* scn) +static INLINE void +scene_setup_embree_geometry_enable_state + (struct s3d_scene* scn, struct geometry* geom) { - struct geometry** geoms; - size_t ngeoms; - size_t i; + ASSERT(scn && geom); + if(geom->is_enabled) { + rtcEnable(scn->rtc_scn, geom->irtc); + } else { + rtcDisable(scn->rtc_scn, geom->irtc); + } +} + +static INLINE void +scene_setup_embree_geometry_filter_function + (struct s3d_scene* scn, struct geometry* geom) +{ + ASSERT(scn && geom && geom->irtc != RTC_INVALID_GEOMETRY_ID); + ASSERT(geom->type == GEOM_MESH); + + if(!geom->data.mesh->filter.func) { + rtcSetIntersectionFilterFunction(scn->rtc_scn, geom->irtc, NULL); + } else { + rtcSetIntersectionFilterFunction(scn->rtc_scn, geom->irtc, filter_wrapper); + rtcSetUserData(scn->rtc_scn, geom->irtc, &geom->data.mesh->filter); + } +} + +static INLINE void +scene_setup_embree_geometry_transform + (struct s3d_scene* scn, struct geometry* geom) +{ + ASSERT(scn && geom && geom->irtc != RTC_INVALID_GEOMETRY_ID); + ASSERT(geom->type == GEOM_INSTANCE); + rtcSetTransform + (scn->rtc_scn, + geom->irtc, + RTC_MATRIX_COLUMN_MAJOR, + geom->data.instance->transform); +} + +static INLINE res_T +scene_setup_embree(struct s3d_scene* scn) +{ + struct htable_shape_iterator it, end; + int rtc_outdated = 0; + res_T res = RES_OK; ASSERT(scn); - ngeoms = darray_geom_size_get(&scn->embree2geoms); - geoms = darray_geom_data_get(&scn->embree2geoms); - FOR_EACH(i, 0, ngeoms) { - if(!geoms[i]) continue; + htable_shape_begin(&scn->shapes, &it); + htable_shape_end(&scn->shapes, &end); + + while(!htable_shape_iterator_eq(&it, &end)) { + struct geometry** pgeom; + struct geometry* geom; + const unsigned* shape_id = htable_shape_iterator_key_get(&it); + htable_shape_iterator_next(&it); + + pgeom = htable_geom_find(&scn->cached_geoms, shape_id); + ASSERT(pgeom != NULL); + geom = *pgeom; + + /* Define whether or not the embree scene is outdated */ + if(geom->embree_outdated_mask) rtc_outdated = 1; + + /* Register the embree geometry */ + res = scene_register_embree_geometry(scn, geom); + if(res != RES_OK) goto error; + + /* Flush the embree geometry states */ + if((geom->embree_outdated_mask & EMBREE_VERTICES) != 0) + scene_setup_embree_geometry_positions(scn, geom); + if((geom->embree_outdated_mask & EMBREE_INDICES) != 0) + scene_setup_embree_geometry_indices(scn, geom); + if((geom->embree_outdated_mask & EMBREE_ENABLE) != 0) + scene_setup_embree_geometry_enable_state(scn, geom); + if((geom->embree_outdated_mask & EMBREE_FILTER_FUNCTION) != 0) + scene_setup_embree_geometry_filter_function(scn, geom); + if((geom->embree_outdated_mask & EMBREE_TRANSFORM) != 0) + scene_setup_embree_geometry_transform(scn, geom); - if(geoms[i]->type == GEOM_INSTANCE) - scene_session_clear(geoms[i]->data.instance->scene); + geom->embree_outdated_mask = 0; + } - geometry_ref_put(geoms[i]); + /* Commit the embree changes */ + if(rtc_outdated) { + rtcCommit(scn->rtc_scn); } + +exit: + return res; +error: darray_geom_clear(&scn->embree2geoms); + goto exit; +} + +static void +scene_session_clear + (struct s3d_scene* scn, + const int internal) /* Is the cleared session internal? */ +{ + ASSERT(scn && scn->session_mask != 0); + + if(internal) { + --scn->nsessions_internal; + } else { + --scn->nsessions; + } + + if(scn->nsessions || scn->nsessions_internal) + return; /* There is still an active session */ + + /* Recursively end the session on instantiated scene */ + if(scn->instances_count != 0) { + struct htable_geom_iterator it, end; + size_t ninstances = 0; + + htable_geom_begin(&scn->cached_geoms, &it); + htable_geom_end(&scn->cached_geoms, &end); + while(ninstances < scn->instances_count /* Early iteration stop */ + && !htable_geom_iterator_eq(&it, &end)) { + struct geometry** pgeom = htable_geom_iterator_data_get(&it); + struct geometry* geom = *pgeom; + htable_geom_iterator_next(&it); + + if(geom->type == GEOM_INSTANCE) { + scene_session_clear(geom->data.instance->scene, 1); + ++ninstances; + } + } + } + + /* Clean-up session data */ + if(scn->session_mask & S3D_TRACE) + darray_geom_clear(&scn->embree2geoms); + if(scn->session_mask & S3D_SAMPLE) + darray_fltui_clear(&scn->cdf); + if(scn->session_mask & S3D_GET_PRIMITIVE) + darray_nprims_cdf_clear(&scn->nprims_cdf); + + /* Reset the session mask */ scn->session_mask = 0; } @@ -280,7 +356,6 @@ scene_register_mesh struct geometry* geom = NULL; size_t iattr; unsigned shape_id; - char upd_pos, upd_ids; res_T res = RES_OK; ASSERT(shape && shape->type == GEOM_MESH); @@ -306,19 +381,15 @@ scene_register_mesh if(geom->irtc != RTC_INVALID_GEOMETRY_ID) { rtcDeleteGeometry(scn->rtc_scn, geom->irtc); geom->irtc = RTC_INVALID_GEOMETRY_ID; - scn->is_rtc_scn_outdated = 1; } mesh_clear(geom->data.mesh); goto exit; } - /* Define which geometry buffers were updated */ - upd_ids = geom->data.mesh->indices != shape->data.mesh->indices; - upd_pos = geom->data.mesh->attribs[S3D_POSITION] != shape->data.mesh->attribs[S3D_POSITION]; - /* Get a reference onto the shape mesh indices */ if(geom->data.mesh->indices != shape->data.mesh->indices) { - if(geom->data.mesh->indices) { + geom->embree_outdated_mask |= EMBREE_INDICES; + if(geom->data.mesh->indices) { /* Release the previous index buffer */ index_buffer_ref_put(geom->data.mesh->indices); geom->data.mesh->indices = NULL; } @@ -329,10 +400,11 @@ scene_register_mesh /* Get a reference onto the shape mesh attribs */ FOR_EACH(iattr, 0, S3D_ATTRIBS_COUNT__) { + geom->embree_outdated_mask |= EMBREE_VERTICES; if(geom->data.mesh->attribs[iattr] == shape->data.mesh->attribs[iattr]) continue; - if(geom->data.mesh->attribs[iattr]) { + if(geom->data.mesh->attribs[iattr]) { /* Release the previous buffer */ vertex_buffer_ref_put(geom->data.mesh->attribs[iattr]); geom->data.mesh->attribs[iattr] = NULL; } @@ -344,6 +416,19 @@ scene_register_mesh geom->data.mesh->attribs_type[iattr] = shape->data.mesh->attribs_type[iattr]; } + /* Update the enable flag */ + if(geom->is_enabled != shape->is_enabled) { + geom->is_enabled = shape->is_enabled; + geom->embree_outdated_mask |= EMBREE_ENABLE; + } + + /* Update the filter function */ + if(geom->data.mesh->filter.func == shape->data.mesh->filter.func + && geom->data.mesh->filter.data == shape->data.mesh->filter.data) { + geom->data.mesh->filter = shape->data.mesh->filter; + geom->embree_outdated_mask |= EMBREE_FILTER_FUNCTION; + } + if(geom->irtc != RTC_INVALID_GEOMETRY_ID) { struct index_buffer* shape_ids = shape->data.mesh->indices; struct index_buffer* geom_ids = geom->data.mesh->indices; @@ -358,23 +443,9 @@ scene_register_mesh if(shape_nids != geom_nids || shape_nverts != geom_nverts) { rtcDeleteGeometry(scn->rtc_scn, geom->irtc); geom->irtc = RTC_INVALID_GEOMETRY_ID; - scn->is_rtc_scn_outdated = 1; } } - res = scene_register_embree_geometry(scn, geom); - if(res != RES_OK) goto error; - - if(upd_pos) { /* Update the Embree vertex buffer if necessary */ - scene_geometry_flush_positions(scn, geom); - } - if(upd_ids) { /* Update the Embree index buffer if necessary */ - scene_geometry_flush_indices(scn, geom); - } - - /* Flush the remaining geometry states */ - scene_geometry_flush_enable_state(scn, geom, shape); - scene_geometry_flush_filter_function(scn, geom, shape); geom->flip_surface = shape->flip_surface; exit: @@ -395,9 +466,15 @@ scene_register_instance res_T res = RES_OK; ASSERT(scn && shape && shape->type == GEOM_INSTANCE); + /* The instance cannot contain instances, i.e. one instancing level is + * supported */ + if(shape->data.instance->scene->instances_count != 0) { + res = RES_BAD_ARG; + goto error; + } + /* Recursively update the scene */ - res = scene_sync(shape->data.instance->scene, - session_mask|S3D_INSTANCE); + res = scene_sync(shape->data.instance->scene, session_mask, 1); if(res != RES_OK) goto error; S3D(shape_get_id(shape, &shape_id)); @@ -415,37 +492,17 @@ scene_register_instance if(res != RES_OK) goto error; geom->name = shape->id.index; } - /* Update the cached instance states */ ASSERT(geom->data.instance->scene == shape->data.instance->scene); - geom->flip_surface = shape->flip_surface; - - /* The instance cannot contain instances, i.e. one instancing level is - * supported */ - if(geom->data.instance->scene->instances_count != 0) { - res = RES_BAD_ARG; - goto error; - } - - /* Create the Embree instance */ - res = scene_register_embree_geometry(scn, geom); - if(res != RES_OK) goto error; - /* Update the Embree instance transformation if necessary */ + /* Update the Embree instance transformation if necessary */ if(!f33_eq(shape->data.instance->transform, geom->data.instance->transform) || !f3_eq(shape->data.instance->transform+9, geom->data.instance->transform+9)) { - + geom->embree_outdated_mask |= EMBREE_TRANSFORM; f33_set(geom->data.instance->transform, shape->data.instance->transform); f3_set(geom->data.instance->transform+9, shape->data.instance->transform+9); - - rtcSetTransform - (scn->rtc_scn, - geom->irtc, - RTC_MATRIX_COLUMN_MAJOR, - geom->data.instance->transform); - scn->is_rtc_scn_outdated = 1; } - scene_geometry_flush_enable_state(scn, geom, shape); + geom->flip_surface = shape->flip_surface; exit: return res; @@ -480,7 +537,6 @@ scene_clear_cached_geometry if(geom->irtc != RTC_INVALID_GEOMETRY_ID) { rtcDeleteGeometry(scn->rtc_scn, geom->irtc); geom->irtc = RTC_INVALID_GEOMETRY_ID; - scn->is_rtc_scn_outdated = 1; } geometry_ref_put(geom); nerased = htable_geom_erase(&scn->cached_geoms, &shape_id); @@ -682,74 +738,82 @@ operator < (const struct nprims_cdf& it, const size_t iprim) } static res_T -scene_sync(struct s3d_scene* scn, const int session_mask) +scene_sync + (struct s3d_scene* scn, + const int session_mask, + const int internal) /* Is the synchronized session internal? */ { - struct htable_shape_iterator it, end; + struct htable_shape_iterator it, it2, end; res_T res = RES_OK; ASSERT(scn); - if((session_mask & S3D_INSTANCE) != 0 - && (scn->session_mask & S3D_INSTANCE) != 0) { - /* The scene was already synced as an instance. Discard sync process */ - return RES_OK; - } else if(scn->session_mask != 0) { - /* The scene cannot be synced several times exepted if it is instantiated */ - res = RES_BAD_OP; - goto error; - } - - if((session_mask & S3D_INSTANCE) == 0 && scn->session_mask != 0) { - res = RES_BAD_OP; - goto error; - } + /* The scene is already synced with respect to te session mask */ + if((scn->session_mask & session_mask) == session_mask) + goto exit; - htable_shape_begin(&scn->shapes, &it); - htable_shape_end(&scn->shapes, &end); + if(scn->session_mask == 0) { /* Are the scene shapes already commited ? */ + /* Commit the shape data to the back-end */ + htable_shape_begin(&scn->shapes, &it); + htable_shape_end(&scn->shapes, &end); + while(!htable_shape_iterator_eq(&it, &end)) { + struct s3d_shape** pshape = htable_shape_iterator_data_get(&it); + struct s3d_shape* shape = *pshape; - while(!htable_shape_iterator_eq(&it, &end)) { - struct s3d_shape** pshape = htable_shape_iterator_data_get(&it); - struct s3d_shape* shape = *pshape; - htable_shape_iterator_next(&it); + switch(shape->type) { + case GEOM_INSTANCE: + res = scene_register_instance(scn, shape, session_mask); + break; + case GEOM_MESH: + res = scene_register_mesh(scn, shape); + break; + default: FATAL("Unreachable code\n"); break; + } + if(res != RES_OK) + goto error; - switch(shape->type) { - case GEOM_INSTANCE: - /* One instancing level is supported */ - if((session_mask & S3D_INSTANCE) != 0) { - res = RES_BAD_ARG; - goto error; - } - res = scene_register_instance(scn, shape, session_mask); - break; - case GEOM_MESH: - res = scene_register_mesh(scn, shape); - break; - default: FATAL("Unreachable code\n"); break; + htable_shape_iterator_next(&it); } - if(res != RES_OK) - goto error; + scene_compute_aabb(scn); } + + /* Setup the scene for the S3D_TRACE session */ + if((session_mask & S3D_TRACE) != 0) { + res = scene_setup_embree(scn); + if(res != RES_OK) goto error; + } + /* Setup the scene for the S3D_SAMPLE session */ if((session_mask & S3D_SAMPLE) != 0) { res = scene_compute_cdf(scn); if(res != RES_OK) goto error; } - if((session_mask & S3D_GET_PRIMITIVE) != 0) { - res = scene_compute_nprims_cdf(scn, 1); - } else { - res = scene_compute_nprims_cdf(scn, 0); - } + /* Setup the scene for the scene_primitive_id/S3D_GET_PRIMITIVE session */ + res = scene_compute_nprims_cdf(scn, (session_mask & S3D_GET_PRIMITIVE)!=0); if(res != RES_OK) goto error; - if((session_mask & S3D_TRACE) != 0 && scn->is_rtc_scn_outdated) { - rtcCommit(scn->rtc_scn); - scn->is_rtc_scn_outdated = 0; - } - scn->session_mask = session_mask; - - scene_compute_aabb(scn); + scn->session_mask |= session_mask; exit: + if(res == RES_OK) { + if(internal) { + ++scn->nsessions_internal; + } else { + ++scn->nsessions; + } + } return res; error: + /* Clear the session on instances that are already registered into the + * current session to invalidate */ + htable_shape_begin(&scn->shapes, &it2); + while(!htable_shape_iterator_eq(&it2, &it)) { + struct s3d_shape** pshape = htable_shape_iterator_data_get(&it2); + struct s3d_shape* shape = *pshape; + htable_shape_iterator_next(&it2); + + if(shape->type == GEOM_INSTANCE) { + scene_session_clear(shape->data.instance->scene, 1); + } + } goto exit; } @@ -762,7 +826,6 @@ scene_release(ref_T* ref) scn = CONTAINER_OF(ref, struct s3d_scene, ref); S3D(scene_clear(scn)); dev = scn->dev; - scene_session_clear(scn); if(scn->rtc_scn) rtcDeleteScene(scn->rtc_scn); htable_shape_release(&scn->shapes); htable_geom_release(&scn->cached_geoms); @@ -959,13 +1022,14 @@ s3d_scene_begin_session(struct s3d_scene* scn, const int session_mask) { if(!scn) return RES_BAD_ARG; + if(!(session_mask&S3D_TRACE) && !(session_mask&S3D_SAMPLE) && !(session_mask&S3D_GET_PRIMITIVE)) { log_error(scn->dev, "%s: no valid session is defined.\n", FUNC_NAME); return RES_BAD_ARG; } - return scene_sync(scn, session_mask); + return scene_sync(scn, session_mask, 0/*Not an internal session*/); } res_T @@ -973,26 +1037,21 @@ s3d_scene_end_session(struct s3d_scene* scn) { if(!scn) return RES_BAD_ARG; - if(scn->session_mask & S3D_INSTANCE) { - log_error(scn->dev, - "%s: the scene session was enabled through scene instantiation.\n", - FUNC_NAME); - return RES_BAD_OP; - } - if(!scn->session_mask) { + + if(!scn->nsessions) { log_error(scn->dev, "%s: the scene has no active session.\n", FUNC_NAME); return RES_BAD_OP; } - scene_session_clear(scn); + ASSERT(scn->session_mask); + scene_session_clear(scn, 0/*Not an internal session*/); return RES_OK; } res_T s3d_scene_get_session_mask(struct s3d_scene* scn, int* session_mask) { - if(!scn || !session_mask) - return RES_BAD_ARG; - *session_mask = scn->session_mask & (~S3D_INSTANCE); + if(!scn || !session_mask) return RES_BAD_ARG; + *session_mask = scn->session_mask; return RES_OK; } diff --git a/src/s3d_scene_c.h b/src/s3d_scene_c.h @@ -92,10 +92,11 @@ struct s3d_scene { size_t instances_count; /* # instances in the scene */ RTCScene rtc_scn; /* Embree scene */ - char is_rtc_scn_outdated; /* Must the embree scene rebuild */ int session_mask; /* Combination of enum s3d_session_flag */ - size_t nsessions; /* # session active onto the scene */ + size_t nsessions; /* # sessions active onto the scene */ + /* # sessions internally enabled (i.e. through instancing) */ + size_t nsessions_internal; struct s3d_device* dev; ref_T ref; diff --git a/src/test_s3d_scene.c b/src/test_s3d_scene.c @@ -160,17 +160,18 @@ main(int argc, char** argv) CHECK(s3d_scene_begin_session(NULL, 0), RES_BAD_ARG); CHECK(s3d_scene_begin_session(scn, 0), RES_BAD_ARG); CHECK(s3d_scene_begin_session(scn, S3D_TRACE|S3D_GET_PRIMITIVE), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_TRACE), RES_BAD_OP); + CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_OK); CHECK(s3d_scene_get_session_mask(scn, &mask), RES_OK); CHECK(mask & S3D_TRACE, S3D_TRACE); - CHECK(mask & S3D_SAMPLE, 0); + CHECK(mask & S3D_SAMPLE, S3D_SAMPLE); CHECK(s3d_scene_clear(scn), RES_BAD_OP); CHECK(s3d_scene_detach_shape(scn, shapes[0]), RES_BAD_OP); CHECK(s3d_scene_end_session(NULL), RES_BAD_ARG); CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_end_session(scn), RES_OK); CHECK(s3d_scene_end_session(scn), RES_BAD_OP); CHECK(s3d_scene_get_session_mask(scn, &mask), RES_OK); @@ -188,7 +189,7 @@ main(int argc, char** argv) CHECK(s3d_scene_attach_shape(scn3, shapes[1]), RES_OK); CHECK(s3d_scene_begin_session(scn2, S3D_SAMPLE|S3D_TRACE), RES_OK); - CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_BAD_OP); + CHECK(s3d_scene_begin_session(scn, S3D_SAMPLE), RES_OK); CHECK(s3d_scene_begin_session(scn3, S3D_SAMPLE), RES_OK); CHECK(s3d_scene_end_session(scn3), RES_OK); @@ -222,6 +223,8 @@ main(int argc, char** argv) CHECK(lower[1] > upper[1], 1); CHECK(lower[2] > upper[2], 1); + CHECK(s3d_scene_end_session(scn), RES_OK); + CHECK(s3d_scene_end_session(scn), RES_BAD_OP); CHECK(s3d_scene_end_session(scn2), RES_OK); CHECK(s3d_scene_compute_area(scn2, &area), RES_BAD_OP);