Parallelize the artifact graph only time suck (#6482)

* parallelize the artifact only time suck

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

make wasm safe

Signed-off-by: Jess Frazelle <github@jessfraz.com>

updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* artifact graph things

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2025-04-26 21:21:26 -07:00
committed by GitHub
parent d0b0365f75
commit 24465cf463
253 changed files with 73547 additions and 55353 deletions

View File

@ -16,18 +16,16 @@ use kcmc::{
use kittycad_modeling_cmds::{self as kcmc};
use uuid::Uuid;
use super::args::TyF64;
#[cfg(feature = "artifact-graph")]
use crate::execution::ArtifactId;
use crate::{
errors::{KclError, KclErrorDetails},
execution::{
types::RuntimeType, ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, Sketch, SketchSurface,
Solid,
},
execution::{types::RuntimeType, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, Sketch, SketchSurface, Solid},
parsing::ast::types::TagNode,
std::Args,
};
use super::args::TyF64;
/// Extrudes by a given amount.
pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
@ -212,6 +210,7 @@ async fn inner_extrude(
solids.push(
do_post_extrude(
sketch,
#[cfg(feature = "artifact-graph")]
id.into(),
length.clone(),
false,
@ -237,7 +236,7 @@ pub(crate) struct NamedCapTags<'a> {
pub(crate) async fn do_post_extrude<'a>(
sketch: &Sketch,
solid_id: ArtifactId,
#[cfg(feature = "artifact-graph")] solid_id: ArtifactId,
length: TyF64,
sectional: bool,
named_cap_tags: &'a NamedCapTags<'a>,
@ -297,6 +296,7 @@ pub(crate) async fn do_post_extrude<'a>(
// are the ones that work with GetOppositeEdge and GetNextAdjacentEdge, aka the n sides in the sweep.
// So here we're figuring out that n number as yielded_sides_count here,
// making sure that circle() calls count but close() don't (no length)
#[cfg(feature = "artifact-graph")]
let count_of_first_set_of_faces_if_sectional = if sectional {
sketch
.paths
@ -311,6 +311,8 @@ pub(crate) async fn do_post_extrude<'a>(
usize::MAX
};
// Only do this if we need the artifact graph.
#[cfg(feature = "artifact-graph")]
for (curve_id, face_id) in face_infos
.iter()
.filter(|face_info| face_info.cap == ExtrusionFaceCapType::None)
@ -327,28 +329,21 @@ pub(crate) async fn do_post_extrude<'a>(
// So, there's no need to await them.
// Instead, the Typescript codebases (which handles WebSocket sends when compiled via Wasm)
// uses this to build the artifact graph, which the UI needs.
let opposite_edge_id = args
.send_modeling_cmd(
exec_state.next_uuid(),
ModelingCmd::from(mcmd::Solid3dGetOppositeEdge {
edge_id: curve_id,
object_id: sketch.id,
face_id,
}),
)
.await?;
let next_adjacent_edge_id = args
.send_modeling_cmd(
exec_state.next_uuid(),
ModelingCmd::from(mcmd::Solid3dGetNextAdjacentEdge {
edge_id: curve_id,
object_id: sketch.id,
face_id,
}),
)
.await?;
//
// Spawn this in the background, because we don't care about the result.
// Only the artifact graph needs at the end.
let args_cloned = args.clone();
let opposite_edge_uuid = exec_state.next_uuid();
let next_adjacent_edge_uuid = exec_state.next_uuid();
let get_all_edge_faces_opposite_uuid = exec_state.next_uuid();
let get_all_edge_faces_next_uuid = exec_state.next_uuid();
#[cfg(test)]
let single_threaded = exec_state.single_threaded;
#[cfg(not(test))]
let single_threaded = false;
// Get faces for original edge
// Since this one is batched we can just run it.
args.batch_modeling_cmd(
exec_state.next_uuid(),
ModelingCmd::from(mcmd::Solid3dGetAllEdgeFaces {
@ -358,36 +353,56 @@ pub(crate) async fn do_post_extrude<'a>(
)
.await?;
// Get faces for opposite edge
if let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::Solid3dGetOppositeEdge(opposite_edge),
} = opposite_edge_id
{
args.batch_modeling_cmd(
exec_state.next_uuid(),
ModelingCmd::from(mcmd::Solid3dGetAllEdgeFaces {
edge_id: opposite_edge.edge,
object_id: sketch.id,
}),
if !single_threaded {
args.ctx
.engine
.async_tasks()
.spawn(get_bg_edge_info_opposite(
args_cloned.clone(),
curve_id,
sketch.id,
face_id,
opposite_edge_uuid,
get_all_edge_faces_opposite_uuid,
single_threaded,
))
.await;
args.ctx
.engine
.async_tasks()
.spawn(get_bg_edge_info_next(
args_cloned,
curve_id,
sketch.id,
face_id,
next_adjacent_edge_uuid,
get_all_edge_faces_next_uuid,
single_threaded,
))
.await;
} else {
get_bg_edge_info_opposite(
args_cloned.clone(),
curve_id,
sketch.id,
face_id,
opposite_edge_uuid,
get_all_edge_faces_opposite_uuid,
single_threaded,
)
.await?;
}
// Get faces for next adjacent edge
if let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::Solid3dGetNextAdjacentEdge(next_adjacent_edge),
} = next_adjacent_edge_id
{
if let Some(edge_id) = next_adjacent_edge.edge {
args.batch_modeling_cmd(
exec_state.next_uuid(),
ModelingCmd::from(mcmd::Solid3dGetAllEdgeFaces {
edge_id,
object_id: sketch.id,
}),
)
.await?;
}
get_bg_edge_info_next(
args_cloned,
curve_id,
sketch.id,
face_id,
next_adjacent_edge_uuid,
get_all_edge_faces_next_uuid,
single_threaded,
)
.await?;
}
}
@ -509,6 +524,7 @@ pub(crate) async fn do_post_extrude<'a>(
// that we passed in to the function, but it's actually the id of the
// sketch.
id: sketch.id,
#[cfg(feature = "artifact-graph")]
artifact_id: solid_id,
value: new_value,
meta: sketch.meta.clone(),
@ -559,3 +575,101 @@ async fn analyze_faces(exec_state: &mut ExecState, args: &Args, face_infos: Vec<
}
faces
}
#[cfg(feature = "artifact-graph")]
async fn send_fn(args: &Args, id: uuid::Uuid, cmd: ModelingCmd, single_threaded: bool) -> Result<(), KclError> {
if single_threaded {
// In single threaded mode, we can safely batch the command.
args.batch_modeling_cmd(id, cmd).await
} else {
// We cannot batch this call, because otherwise it might batch after say
// a shell that makes this edge no longer relevant.
args.send_modeling_cmd(id, cmd).await.map(|_| ())
}
}
#[cfg(feature = "artifact-graph")]
#[allow(clippy::too_many_arguments)]
async fn get_bg_edge_info_next(
args: Args,
curve_id: uuid::Uuid,
sketch_id: uuid::Uuid,
face_id: uuid::Uuid,
edge_uuid: uuid::Uuid,
get_all_edge_faces_uuid: uuid::Uuid,
single_threaded: bool,
) -> Result<(), KclError> {
let next_adjacent_edge_id = args
.send_modeling_cmd(
edge_uuid,
ModelingCmd::from(mcmd::Solid3dGetNextAdjacentEdge {
edge_id: curve_id,
object_id: sketch_id,
face_id,
}),
)
.await?;
// Get faces for next adjacent edge
if let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::Solid3dGetNextAdjacentEdge(next_adjacent_edge),
} = next_adjacent_edge_id
{
if let Some(edge_id) = next_adjacent_edge.edge {
send_fn(
&args,
get_all_edge_faces_uuid,
ModelingCmd::from(mcmd::Solid3dGetAllEdgeFaces {
edge_id,
object_id: sketch_id,
}),
single_threaded,
)
.await?;
}
}
Ok(())
}
#[cfg(feature = "artifact-graph")]
#[allow(clippy::too_many_arguments)]
async fn get_bg_edge_info_opposite(
args: Args,
curve_id: uuid::Uuid,
sketch_id: uuid::Uuid,
face_id: uuid::Uuid,
edge_uuid: uuid::Uuid,
get_all_edge_faces_uuid: uuid::Uuid,
single_threaded: bool,
) -> Result<(), KclError> {
let opposite_edge_id = args
.send_modeling_cmd(
edge_uuid,
ModelingCmd::from(mcmd::Solid3dGetOppositeEdge {
edge_id: curve_id,
object_id: sketch_id,
face_id,
}),
)
.await?;
// Get faces for opposite edge
if let OkWebSocketResponseData::Modeling {
modeling_response: OkModelingCmdResponse::Solid3dGetOppositeEdge(opposite_edge),
} = opposite_edge_id
{
send_fn(
&args,
get_all_edge_faces_uuid,
ModelingCmd::from(mcmd::Solid3dGetAllEdgeFaces {
edge_id: opposite_edge.edge,
object_id: sketch_id,
}),
single_threaded,
)
.await?;
}
Ok(())
}