forked from 1b-agent/opencadstudio-upstream
Record release-build boolean timings and a recommendation
Times brep::combine in release against the pinned cadkernel: about 1-2 s for curved operands, 1-2 ms for planar, versus 23 s in debug. Records the kernel's frequent refusals, the host-thread and 30 s script-budget implications, and recommends a synchronous Boolean operation that surfaces refusals. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9cd6653a58
commit
69047549e7
2 files changed
+64
-4
No files matched your search
@@ -109,10 +109,10 @@ is refused with a clear message instead of being rewritten.
|
|||||||
|
|
||||||
## Risks and open questions
|
## Risks and open questions
|
||||||
|
|
||||||
1. **Boolean speed.** About 23 s each in a debug build. Time them in release
|
1. **Boolean speed.** Measured in release, see "Boolean timings" below: about
|
||||||
before deciding whether they can run on the host thread (the GUI BOOLEAN
|
1 to 2.2 s for a box and a cylinder, 0.2 s for two spheres and 1 to 2 ms for
|
||||||
command has the same cost) or need a background task. Everything else is
|
planar-only operands, against 23 s in a debug build. The kernel also refuses
|
||||||
instant.
|
many curved cases, which matters more than speed.
|
||||||
2. **Interoperability cannot be verified here.** `solid_to_sat` is verified by
|
2. **Interoperability cannot be verified here.** `solid_to_sat` is verified by
|
||||||
OCS's own lifter. Whether AutoCAD accepts the SAT/SAB that OCS writes is not
|
OCS's own lifter. Whether AutoCAD accepts the SAT/SAB that OCS writes is not
|
||||||
testable without AutoCAD, so completion should be scoped as "OCS round trip",
|
testable without AutoCAD, so completion should be scoped as "OCS round trip",
|
||||||
@@ -137,3 +137,62 @@ is refused with a clear message instead of being rewritten.
|
|||||||
Phase 1 alone would move Solid3D from blocked to complete-in-OCS.
|
Phase 1 alone would move Solid3D from blocked to complete-in-OCS.
|
||||||
|
|
||||||
**Status (2026-09-20): phase 1 is done.** `HostApi::solid_operation` (create primitive and rigid transform), the `doc.solids` Python API and `audit_python_solid3d_lifecycle_over_real_ipc` are in; Solid3D is complete, Body moves through the same channel. Design B was implemented as recommended. Region creation from a closed profile followed (`RegionFromProfile`). Plane surfaces and extrusions (`SurfaceFromProfile`, `Extrude`) followed. Only booleans remain, plus lofted, revolved, swept and NURBS surfaces.
|
**Status (2026-09-20): phase 1 is done.** `HostApi::solid_operation` (create primitive and rigid transform), the `doc.solids` Python API and `audit_python_solid3d_lifecycle_over_real_ipc` are in; Solid3D is complete, Body moves through the same channel. Design B was implemented as recommended. Region creation from a closed profile followed (`RegionFromProfile`). Plane surfaces and extrusions (`SurfaceFromProfile`, `Extrude`) followed. Only booleans remain, plus lofted, revolved, swept and NURBS surfaces.
|
||||||
|
|
||||||
|
## Boolean timings (release build)
|
||||||
|
|
||||||
|
Measured 2026-09-20 in a scratch crate built with `--release` (opt-level 3)
|
||||||
|
against the same cadkernel revision OCS pins (`6f046af`, features `acis` and
|
||||||
|
`offset`), calling exactly what OCS's `solid_model::boolean_result` calls:
|
||||||
|
`brep::operation_tolerance` then `brep::combine`. A debug build of OCS is about
|
||||||
|
22 times slower, so booleans must never be exercised in a debug test run. The
|
||||||
|
scratch program is not in the repository; to repeat it, depend on cadkernel at
|
||||||
|
that revision and time `brep::combine` on `brep::make::{cuboid, cylinder,
|
||||||
|
sphere, torus}` operands.
|
||||||
|
|
||||||
|
| Operation | Time | Result |
|
||||||
|
|---|---|---|
|
||||||
|
| box union / subtract / intersect cylinder (10x6x4 box, r=2 cylinder) | 1.05 to 1.08 s each, identical on repeat runs | ok |
|
||||||
|
| same, operands scaled 10x and 100x | 2.21 s and 2.21 s | ok |
|
||||||
|
| box union / subtract box (planar only) | 0.001 to 0.002 s | ok |
|
||||||
|
| sphere union sphere (overlapping) | 0.22 s | ok |
|
||||||
|
| sphere subtract sphere (overlapping) | 0.23 s | refused `CutRefused` |
|
||||||
|
| box subtract sphere (sphere cutting the box faces) | 1.28 s | refused `Coincident` |
|
||||||
|
| sphere union torus (disjoint) | under 1 ms | refused `NoClosedForm` |
|
||||||
|
| torus subtract cylinder through the ring | under 1 ms | refused `NoClosedForm` |
|
||||||
|
| drill a first cylinder hole in a box | 0.03 s | ok |
|
||||||
|
| drill a second hole in the drilled box | under 1 ms | refused `CutRefused` |
|
||||||
|
|
||||||
|
What this means:
|
||||||
|
|
||||||
|
- **Speed is acceptable.** A curved boolean costs 0.2 to 2.2 s, the same cost as
|
||||||
|
the GUI BOOLEAN command, and planar work is instant. The 23 s seen earlier was
|
||||||
|
a debug-build artifact.
|
||||||
|
- **It blocks the host thread.** The plugin's `PY_RUN` is a synchronous dispatch
|
||||||
|
and nested host requests run inline, so a script freezes the UI while a
|
||||||
|
boolean runs. One to two seconds per operation is tolerable; a script with
|
||||||
|
dozens is not.
|
||||||
|
- **The whole script has a budget.** A `Dispatch` call uses the 30 s default
|
||||||
|
timeout (`OCS_PLUGIN_CALL_TIMEOUT_SECS`, with a 10 s floor), and nested host
|
||||||
|
work counts against one deadline. Roughly fifteen curved booleans in a single
|
||||||
|
script would exceed it.
|
||||||
|
- **Refusals are the real limit.** The kernel says no often: coincident faces, a
|
||||||
|
cut it has no closed form for, torus operands, and, surprisingly, a second
|
||||||
|
cut in a body that was already drilled. Refusals are fast and leave the inputs
|
||||||
|
intact, which is the behavior the API already relies on, but it means booleans
|
||||||
|
will be a partial feature that fails with a reason, not a general one.
|
||||||
|
- **Timing is not proportional to size.** Scaling the operands 10x doubled the
|
||||||
|
time, 100x changed nothing more, so the cost is dominated by the surface
|
||||||
|
intersection setup rather than the geometry size.
|
||||||
|
|
||||||
|
### Recommendation
|
||||||
|
|
||||||
|
Add `SolidOperation::Boolean { first, second, operation, layer }` and run it
|
||||||
|
synchronously on the host thread, surfacing the kernel's refusal (`Coincident`,
|
||||||
|
`CutRefused`, `NoClosedForm`, ...) verbatim in the error and leaving both
|
||||||
|
operands untouched on failure. Build the result with the existing
|
||||||
|
`entity_with_boolean_body` path so it inherits the lossless verification.
|
||||||
|
Test it with **planar** operands (millisecond cost, safe in debug) plus one
|
||||||
|
`#[ignore]` curved case that documents the release-only runtime. Document the
|
||||||
|
30 s script budget and that chained cuts on a drilled body are often refused.
|
||||||
|
Do not move booleans to a background thread yet: it would need cancellation and
|
||||||
|
undo coordination for a cost that is one to two seconds.
|
||||||
@@ -180,3 +180,4 @@ exact queue above and update statuses with evidence after each increment.
|
|||||||
- Built the cadkernel operation channel and completed `Solid3D` (create and rigid move). `HostApi::solid_operation` is additive (default "unsupported"), so older hosts and plugins are unaffected; it travels as `PluginRequest::SolidOperation` and `PluginResponse::SolidResult`, and the host records its own undo step only after validation so a refused operation leaves nothing behind. The Python surface is `doc.solids.*`. Every result is verified lossless by the kernel path before it is accepted and a payload that will not lift is refused untouched. `Body` moves through the same channel and `Region` is accepted but untested. Booleans, Region creation from curves and Surface remain, in that order; booleans wait on release-build timing. Solid3D, Body and Region are now mapped read-only with the opaque payload excluded and preserved.
|
- Built the cadkernel operation channel and completed `Solid3D` (create and rigid move). `HostApi::solid_operation` is additive (default "unsupported"), so older hosts and plugins are unaffected; it travels as `PluginRequest::SolidOperation` and `PluginResponse::SolidResult`, and the host records its own undo step only after validation so a refused operation leaves nothing behind. The Python surface is `doc.solids.*`. Every result is verified lossless by the kernel path before it is accepted and a payload that will not lift is refused untouched. `Body` moves through the same channel and `Region` is accepted but untested. Booleans, Region creation from curves and Surface remain, in that order; booleans wait on release-build timing. Solid3D, Body and Region are now mapped read-only with the opaque payload excluded and preserved.
|
||||||
- Completed `Region`. `SolidOperation::RegionFromProfile` reuses the REGION command's kernel calls and `doc.solids.region` exposes it; the lifecycle test creates regions from real circle and polyline entities and covers deletion of the source, refusals, a move, both formats and undo. Along the way `create_entity` now accepts `(x, y, z)` tuples for point properties (decided from the schema's property types) and every generated float setter accepts Python ints, so `create_entity('Circle', center=(0, 0, 0), radius=5)` works. Left: booleans (release timing first), holes and open-curve merging for regions, and `Surface`.
|
- Completed `Region`. `SolidOperation::RegionFromProfile` reuses the REGION command's kernel calls and `doc.solids.region` exposes it; the lifecycle test creates regions from real circle and polyline entities and covers deletion of the source, refusals, a move, both formats and undo. Along the way `create_entity` now accepts `(x, y, z)` tuples for point properties (decided from the schema's property types) and every generated float setter accepts Python ints, so `create_entity('Circle', center=(0, 0, 0), radius=5)` works. Left: booleans (release timing first), holes and open-curve merging for regions, and `Surface`.
|
||||||
- Completed `Surface` for planes, extruded open profiles, isolines and rigid moves. `SurfaceFromProfile` and `Extrude` join the kernel operations (`doc.solids.surface` and `doc.solids.extrude`); `Extrude` also gives Solid3D extrusions from closed profiles, checked against analytic volumes. The test found and fixed a real bug: Surface's `kind` field silently overwrote the entity-type `kind` key in Python dicts, so it is exposed as `surface_kind`, and a new uniqueness test guards the whole catalog. Transform now accepts surfaces (a plane stays a plane; other kinds turn generic). Left: booleans (release timing first), regions with holes, and lofted, revolved, swept and NURBS surfaces. SectionSymbol is the only unmapped kind.
|
- Completed `Surface` for planes, extruded open profiles, isolines and rigid moves. `SurfaceFromProfile` and `Extrude` join the kernel operations (`doc.solids.surface` and `doc.solids.extrude`); `Extrude` also gives Solid3D extrusions from closed profiles, checked against analytic volumes. The test found and fixed a real bug: Surface's `kind` field silently overwrote the entity-type `kind` key in Python dicts, so it is exposed as `surface_kind`, and a new uniqueness test guards the whole catalog. Transform now accepts surfaces (a plane stays a plane; other kinds turn generic). Left: booleans (release timing first), regions with holes, and lofted, revolved, swept and NURBS surfaces. SectionSymbol is the only unmapped kind.
|
||||||
|
- Timed booleans in a release build (details in [cadkernel-body-path.md](cadkernel-body-path.md)). A curved box-and-cylinder boolean takes about 1 to 2.2 s, overlapping spheres 0.2 s and planar operands 1 to 2 ms, against 23 s in a debug build. The kernel refuses many curved cases quickly with a reason (`Coincident`, `CutRefused`, `NoClosedForm`), including a second cut in an already drilled box. Recommendation: add a synchronous `SolidOperation::Boolean` that surfaces the refusal and leaves the operands untouched, test it with planar operands plus one ignored curved case, and note the 30 s per-script budget. No code changed in this step.
|
||||||
Reference in new issue
Block a user