commit 9182baeae54af2ef2b68ef305d30dfc755d103d8
parent 11f03a751b240b37bd6a5a57afe237a92473a536
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Mon, 8 Sep 2025 17:42:05 +0200
Rework on the auto-sync feature
Try to limit calls to synchronize as it is flagged as expensive while
ensuring correctness.
Diffstat:
6 files changed, 227 insertions(+), 30 deletions(-)
diff --git a/Makefile b/Makefile
@@ -131,7 +131,8 @@ TEST_SRC =\
src/test_export2.c\
src/test_lifetime.c\
src/test_partition.c\
- src/test_periodic.c
+ src/test_periodic.c\
+ src/test_sync.c
TEST_OBJ = $(TEST_SRC:.c=.o)
TEST_DEP = $(TEST_SRC:.c=.d)
@@ -185,5 +186,6 @@ test_export2 \
test_lifetime \
test_partition \
test_periodic \
+test_sync \
: config.mk scad-local.pc $(LIBNAME)
$(CC) $(CFLAGS_EXE) -o $@ src/$@.o $(LDFLAGS_EXE) $(SCAD_LIBS) $(RSYS_LIBS) -lm
diff --git a/src/scad.c b/src/scad.c
@@ -189,7 +189,9 @@ error:
goto exit;
}
-/* Accumulate tags of geometry in tags */
+/* Accumulate the 2D tags of geometry (if it is 2D) or the tags of its boundary
+ * (if it is 3D).
+ * Trigger a call to sync_device() */
res_T
get_2d_tags
(struct scad_geometry* geometry,
@@ -208,6 +210,7 @@ get_2d_tags
data = geometry->gmsh_dimTags;
ASSERT(sz % 2 == 0);
+ ERR(sync_device());
for(i = 0; i < sz; i += 2) {
int dim = data[i];
int tag = data[i+1];
@@ -265,7 +268,7 @@ scad_synchronize
}
gmshModelOccSynchronize(&ierr);
- get_device()->need_synchro = dev->options.Misc.DebugAutoSync;
+ dev->need_synchro = dev->options.Misc.DebugAutoSync;
ERR(gmsh_err_to_res_T(ierr));
exit:
@@ -291,8 +294,8 @@ scad_run_ui(void)
goto error;
}
- if(dev->options.Misc.SynchronizeOnRunUI && get_device()->need_synchro) {
- ERR(scad_synchronize());
+ if(dev->options.Misc.SynchronizeOnRunUI) {
+ ERR(sync_device());
}
gmshFltkRun(&ierr);
@@ -334,6 +337,7 @@ scad_scene_write
}
ERR(check_device(FUNC_NAME));
+ ERR(sync_device());
gmshWrite(name, &ierr);
ERR(gmsh_err_to_res_T(ierr));
@@ -938,6 +942,7 @@ scad_scene_mesh
ERR(check_device(FUNC_NAME));
+ ERR(sync_device());
gmshModelMeshGenerate(2, &ierr);
ERR(gmsh_err_to_res_T(ierr));
diff --git a/src/scad_device.c b/src/scad_device.c
@@ -80,13 +80,10 @@ device_release(struct scad_device* dev)
MEM_RM(dev->allocator, dev);
htable_geometries_release(&tmp);
-#ifndef NDEBUG
- {
- int ierr;
- gmshModelOccSynchronize(&ierr);
+ if(dev->options.Misc.DebugEmptyContext) {
+ /* After releasing all star-cad stuff, gmsh and OCC contexts must be empty */
return check_empty_gmsh_occ(dev);
}
-#endif
return res;
}
@@ -149,10 +146,6 @@ check_device
goto error;
}
- if(g_device->need_synchro) {
- ERR(scad_synchronize());
- }
-
if(g_device->options.Misc.RunUIAtEachStep) {
ERR(scad_run_ui());
}
@@ -169,6 +162,21 @@ error:
goto exit;
}
+res_T
+sync_device(void)
+{
+ res_T res = RES_OK;
+
+ if(g_device->need_synchro) {
+ ERR(scad_synchronize()); /* Reset need_synchro according to options */
+ }
+
+exit:
+ return res;
+error:
+ goto exit;
+}
+
struct scad_device*
get_device
(void)
@@ -339,6 +347,7 @@ do_device_tags_ref_get
struct tag_desc d;
tag_desc_init(dev->allocator, &d);
ERR(htable_tags2desc_set(t2d, &tag, &d));
+ dev->need_synchro = 1;
if(log) {
logger_print(dev->logger, log_type, "New tag %d.%d (count set to 1).\n",
dim, tag);
@@ -416,6 +425,7 @@ do_device_tags_ref_put
dim, tag);
}
gmshModelOccRemove(dimTags+i, 2, 0, &ierr);
+ dev->need_synchro = 1;
ERR(gmsh_err_to_res_T(ierr));
break;
case Scad_delete_recursive:
@@ -424,6 +434,7 @@ do_device_tags_ref_put
dim, tag);
}
gmshModelOccRemove(dimTags+i, 2, 1, &ierr);
+ dev->need_synchro = 1;
ERR(gmsh_err_to_res_T(ierr));
break;
default: FATAL("Invalid enum value");
@@ -537,13 +548,13 @@ check_empty_gmsh_occ(struct scad_device* dev)
for(d = 3; d >= 0; d--) {
gmshFree(dimTags);
dimTags = NULL;
- gmshModelGetEntities(&dimTags, &dimTags_n, d, &ierr);
+ gmshModelOccGetEntities(&dimTags, &dimTags_n, d, &ierr);
ASSERT(dimTags_n % 2 == 0);
if(dimTags_n == 0)
continue;
found = 1;
log_error(dev,
- "There are %ld unreferenced gmsh entities of dim %d from an empty star-cad context%c\n",
+ "There are %ld unreferenced Open-Cascade entities of dim %d from an empty star-cad context%c\n",
dimTags_n / 2, d, (d > 1 ? ':' : '.'));
if(d < 2) continue;
if(!msg_initialized) str_init(dev->allocator, &msg);
@@ -559,17 +570,19 @@ check_empty_gmsh_occ(struct scad_device* dev)
goto error;
}
+ ERR(sync_device());
+
found = 0;
for(d = 3; d >= 0; d--) {
gmshFree(dimTags);
dimTags = NULL;
- gmshModelOccGetEntities(&dimTags, &dimTags_n, d, &ierr);
+ gmshModelGetEntities(&dimTags, &dimTags_n, d, &ierr);
ASSERT(dimTags_n % 2 == 0);
if(dimTags_n == 0)
continue;
found = 1;
log_error(dev,
- "There are %ld unreferenced Open-Cascade entities of dim %d from an empty star-cad context%c\n",
+ "There are %ld unreferenced gmsh entities of dim %d from an empty star-cad context%c\n",
dimTags_n / 2, d, (d > 1 ? ':' : '.'));
if(d < 2) continue;
if(!msg_initialized) str_init(dev->allocator, &msg);
diff --git a/src/scad_device.h b/src/scad_device.h
@@ -198,6 +198,9 @@ LOCAL_SYM res_T
check_device
(const char* function_name);
+LOCAL_SYM res_T
+sync_device(void);
+
LOCAL_SYM struct scad_device*
get_device
(void);
diff --git a/src/scad_geometry.c b/src/scad_geometry.c
@@ -150,7 +150,6 @@ geometry_create
ref_init(&geom->ref);
str_init(allocator, &geom->name);
ERR(htable_geometries_set(&dev->allgeom, &geom, &one));
- dev->need_synchro = 1;
if(dev->options.Misc.LogRefCounting & SCAD_LOG_GEOMETRY) {
if(str_is_empty(&geom->name)) {
@@ -389,6 +388,8 @@ error:
goto exit;
}
+/* Recursivelly get the tags of geometries, possibly down to dim 0.
+ * Trigger a call to sync_device() */
static res_T
gather_tags_recursive
(struct scad_geometry** geometries,
@@ -425,6 +426,7 @@ gather_tags_recursive
/* Recursively build result by dimension and list constituents,
* begining with dim==3 */
+ ERR(sync_device());
for(dim = 3; dim >= down_to_dim; dim--) {
size_t c = 0;
sz[dim] = 2 * htable_tags_size_get(t+dim);
@@ -517,7 +519,6 @@ geometry_release(ref_T* ref)
size_t n;
ASSERT(ref);
- dev->need_synchro = 1;
geom = CONTAINER_OF(ref, struct scad_geometry, ref);
CHK(RES_OK == device_unregister_tags(dev->log, dev->log_type, geom));
MEM_RM(allocator, geom->gmsh_dimTags);
@@ -790,11 +791,10 @@ scad_geometry_set_visibility
data = geom->gmsh_dimTags;
sz = geom->gmsh_dimTags_n;
+ ERR(sync_device());
gmshModelSetVisibility(data, sz, visible, recursive, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- get_device()->need_synchro = 1;
-
exit:
return res;
error:
@@ -822,7 +822,6 @@ scad_geometries_clear_mesh
allocator = dev->allocator;
ERR(gather_tags_recursive(geometries, geometries_count, 0, data, sz));
- ERR(sync_device());
for(dim = 3; dim >= 0; dim--) {
/* Cannot clear the mesh of lower dim entities if linked to higher dim
* entities with uncleared mesh: start from the higher dim down to 0 */
@@ -2003,6 +2002,7 @@ scad_geometries_common_boundaries
* (even if the refcounting stuff can manage duplicates) */
ERR(process_tag_list(data1, sz1, data2, sz2, UNIQUE_TAGS, &unique, &u_sz));
+ ERR(sync_device());
gmshModelGetBoundary(data1, sz1, &bound1, &n1, 1, 0, 0, &ierr);
ERR(gmsh_err_to_res_T(ierr));
gmshModelGetBoundary(data2, sz2, &bound2, &n2, 1, 0, 0, &ierr);
@@ -2141,7 +2141,6 @@ scad_geometry_rotate
gmshModelOccRotate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(pt),
SPLIT3(axis), angle, &ierr);
- get_device()->need_synchro = 1;
ERR(gmsh_err_to_res_T(ierr));
exit:
@@ -2203,7 +2202,6 @@ scad_geometry_extrude
gmshModelOccExtrude(geom->gmsh_dimTags, geom->gmsh_dimTags_n, SPLIT3(dxdydz),
&tagout, &tagoutn, NULL, 0, NULL, 0, 0, &ierr);
ERR(gmsh_err_to_res_T(ierr));
- get_device()->need_synchro = 1;
/* Output includes both the 3D result and its 2D constituents.
* Keep only 3D entities. */
@@ -2386,7 +2384,6 @@ scad_geometry_copy
sz1 = geom->gmsh_dimTags_n;
data1 = geom->gmsh_dimTags;
gmshModelOccCopy(data1, sz1, &tagout, &tagoutn, &ierr);
- get_device()->need_synchro = 1;
ASSERT(tagoutn % 2 == 0);
ERR(gmsh_err_to_res_T(ierr));
@@ -2474,7 +2471,6 @@ scad_geometry_translate
gmshModelOccTranslate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(dxdydz),
&ierr);
- get_device()->need_synchro = 1;
ERR(gmsh_err_to_res_T(ierr));
exit:
@@ -2577,8 +2573,6 @@ scad_geometries_partition
ERR(gmsh_err_to_res_T(ierr));
ASSERT(sz == 2*mapnn); /* Because input tags where deduplicated */
- get_device()->need_synchro = 1;
-
/* Check first if there was an overlapping problem */
if(!(flags & SCAD_ALLOW_OVERLAPPING)) {
/* No overlapping means that each tag in geometries is translated into a
@@ -2931,6 +2925,7 @@ scad_geometries_boundary
allocator = dev->allocator;
ERR(gather_tags(geometries, geometries_count, &data, &sz));
+ ERR(sync_device());
gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr);
ERR(gmsh_err_to_res_T(ierr));
@@ -3301,7 +3296,7 @@ scad_geometries_set_mesh_size_modifier
}
}
- gmshModelMeshSetSizeCallback( (some ? size_callback : NULL), NULL, &ierr);
+ gmshModelMeshSetSizeCallback((some ? size_callback : NULL), NULL, &ierr);
ERR(gmsh_err_to_res_T(ierr));
exit:
diff --git a/src/test_sync.c b/src/test_sync.c
@@ -0,0 +1,179 @@
+/* Copyright (C) 2022-2025 |Méso|Star> (contact@meso-star.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "scad.h"
+#include "scad_geometry.h"
+#include "test_common.h"
+
+#include <rsys/rsys.h>
+#include <rsys/str.h>
+#include <rsys/math.h>
+#include <rsys/double3.h>
+#include <rsys/mem_allocator.h>
+#include <rsys/dynamic_array_double.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+static void
+get_position
+ (const size_t ivert, double pos[2], void* data)
+{
+ double* coord = data;
+ ASSERT(pos && coord);
+ pos[0] = coord[2*ivert];
+ pos[1] = coord[1+2*ivert];
+}
+
+int
+main(int argc, char* argv[])
+{
+ res_T res = RES_OK;
+ const double p1[3] = {0, 0, 0};
+ const double p2[3] = {0.25, 0.25, 0.8};
+ const double p3[3] = {0.85, 0, 0};
+ const double p4[3] = {1, 0, 0};
+ const double d0[3] = {0.1, 0.1, 0.1};
+ const double d1[3] = {1, 1, 1};
+ const double d2[3] = {1, 1, 0};
+ const double s[3] = {1, 1, 1};
+ double coord[] = {0, 1.6, 0.5, 0.9, 0.8, 0.6};
+ struct scad_geometry* geom1 = NULL;
+ struct scad_geometry* geom2 = NULL;
+ struct scad_geometry* geom = NULL;
+ struct scad_geometry* poly = NULL;
+ struct scad_geometry** geom_array = NULL;
+ struct scad_geometry* geoms[2];
+ struct scad_geometry* out_geoms[2];
+ struct mem_allocator allocator;
+ struct darray_double trg;
+ struct scad_options options = SCAD_DEFAULT_OPTIONS;
+ double m, tmp[3], tmp2[3];
+ size_t i, c;
+ int e;
+
+ (void)argc; (void)argv;
+
+ OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator));
+
+ OK(scad_initialize(NULL, &allocator, 3));
+ options.Mesh.MeshSizeExtendFromBoundary = 0;
+ options.Mesh.MeshSizeFromPoints = 0;
+ options.Mesh.Algorithm = SCAD_INITIAL_MESH_ONLY;
+ OK(scad_set_options(&options));
+
+ OK(scad_add_box(p1, d1, &geom));
+ OK(scad_scene_mesh());
+ darray_double_init(&allocator, &trg);
+ OK(scad_stl_get_data(geom, &trg));
+ CHK(12 * 9 == darray_double_size_get(&trg));
+ darray_double_release(&trg);
+ OK(scad_geometry_ref_put(geom));
+
+ OK(scad_add_box(p1, d1, &geom1));
+ OK(scad_add_box(p2, d1, &geom2));
+ OK(scad_geometries_cut(&geom1, 1, &geom2, 1, &geom));
+ OK(scad_geometry_ref_put(geom1));
+ OK(scad_geometry_ref_put(geom2));
+ OK(scad_scene_mesh());
+ darray_double_init(&allocator, &trg);
+ OK(scad_stl_get_data(geom, &trg));
+ CHK(24 * 9 == darray_double_size_get(&trg));
+ darray_double_release(&trg);
+ OK(scad_geometry_ref_put(geom));
+
+ OK(scad_add_polygon(get_position, coord, 0, 3, &poly));
+ OK(scad_geometry_extrude(poly, d1, &geom));
+ OK(scad_geometry_ref_put(poly));
+ OK(scad_scene_mesh());
+ darray_double_init(&allocator, &trg);
+ OK(scad_stl_get_data(geom, &trg));
+ CHK(8 * 9 == darray_double_size_get(&trg));
+ darray_double_release(&trg);
+ OK(scad_geometry_ref_put(geom));
+
+ OK(scad_add_box(p1, d1, &geom1));
+ OK(scad_geometry_translate(geom1, d1, &geom));
+ OK(scad_geometry_ref_put(geom1));
+ OK(scad_scene_mesh());
+ darray_double_init(&allocator, &trg);
+ OK(scad_stl_get_data(geom, &trg));
+ CHK(12 * 9 == darray_double_size_get(&trg));
+ darray_double_release(&trg);
+ OK(scad_geometry_ref_put(geom));
+
+ OK(scad_add_box(p1, d1, &geom));
+ OK(scad_geometry_set_visibility(geom, 0, 1));
+ OK(scad_scene_mesh());
+ darray_double_init(&allocator, &trg);
+ OK(scad_stl_get_data(geom, &trg));
+ CHK(0 == darray_double_size_get(&trg));
+ darray_double_release(&trg);
+ OK(scad_geometry_ref_put(geom));
+
+ OK(scad_add_box(p1, d1, &geom));
+ OK(scad_scene_mesh());
+ OK(scad_geometries_clear_mesh(&geom, 1));
+ darray_double_init(&allocator, &trg);
+ OK(scad_stl_get_data(geom, &trg));
+ CHK(0 == darray_double_size_get(&trg));
+ darray_double_release(&trg);
+ OK(scad_geometry_ref_put(geom));
+
+ options.Mesh.Algorithm = SCAD_MESHADAPT;
+ options.Mesh.MeshSizeFromPoints = 1;
+ options.Mesh.MeshSizeExtendFromBoundary = 1;
+ OK(scad_set_options(&options));
+
+ OK(scad_add_box(p1, d1, &geom));
+ OK(scad_scene_mesh());
+ darray_double_init(&allocator, &trg);
+ OK(scad_stl_get_data(geom, &trg));
+ c = darray_double_size_get(&trg);
+ darray_double_clear(&trg);
+ printf("%ld trg\n", c/9);
+
+ OK(scad_geometries_clear_mesh(&geom, 1));
+ OK(scad_stl_get_data(geom, &trg));
+ CHK(0 == darray_double_size_get(&trg));
+
+ OK(scad_geometries_set_mesh_size_modifier(&geom, 1, SCAD_SIZE_FACTOR, 0.25));
+ OK(scad_scene_mesh());
+ OK(scad_stl_get_data(geom, &trg));
+ printf("%ld trg\n", darray_double_size_get(&trg)/9);
+ CHK(c < darray_double_size_get(&trg));
+ darray_double_release(&trg);
+ OK(scad_geometry_ref_put(geom));
+
+ options.Mesh.MeshSizeExtendFromBoundary = 0;
+ options.Mesh.MeshSizeFromPoints = 0;
+ options.Mesh.Algorithm = SCAD_INITIAL_MESH_ONLY;
+ OK(scad_set_options(&options));
+
+#if 0
+set mesh size cb : besoin de sync ?
+#endif
+
+ options.Misc.LogRefCounting = SCAD_LOG_DIMTAGS_ONLY_UNDELETED;
+ OK(scad_set_options(&options));
+
+ OK(scad_finalize());
+
+ check_memory_allocator(&allocator);
+ mem_shutdown_proxy_allocator(&allocator);
+ CHK(mem_allocated_size() == 0);
+
+ return (res == RES_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
+}