减少切分状态写入的重复克隆

让状态写入接口借用状态并在阻塞任务中写入序列化字节

保留 schema 与大小校验,避免每批次复制完整状态树
This commit is contained in:
2026-09-12 13:16:25 +08:00
parent 0726106818
commit d8a4e35232
3 changed files with 25 additions and 33 deletions
@@ -42,30 +42,19 @@ pub fn project_relative_path(root: &Path, path: &Path) -> Result<String, String>
Ok(value)
}
pub async fn write_separation_state(path: PathBuf, state: SeparationState) -> Result<(), String> {
pub async fn write_separation_state(path: PathBuf, state: &SeparationState) -> Result<(), String> {
if state.schema_version != SEPARATION_STATE_SCHEMA_VERSION {
app_log!("ui_separation.error stage=state_write reason=schema_mismatch");
return Err("不支持的 separation state schema".to_string());
}
let state = serde_json::to_vec_pretty(state)
.map_err(|error| format!("序列化 separation state 失败:{error}"))?;
tokio::task::spawn_blocking(move || write_separation_state_blocking(&path, &state))
.await
.map_err(|error| format!("写入 separation state 任务失败:{error}"))?
}
fn write_separation_state_blocking(path: &Path, state: &SeparationState) -> Result<(), String> {
if state.schema_version != SEPARATION_STATE_SCHEMA_VERSION {
app_log!("ui_separation.error stage=state_write reason=schema_mismatch");
return Err("不支持的 separation state schema".to_string());
}
app_log!(
"ui_separation.state_write.start file={} trees={} bound={} problematic={}",
path.file_name()
.and_then(|name| name.to_str())
.unwrap_or("<unknown>"),
state.trees.len(),
state.bound.len(),
state.problematic_nodes.len()
);
let bytes = serde_json::to_vec_pretty(state).map_err(|error| {
app_log!("ui_separation.error stage=state_write reason=serialize error={error}");
format!("序列化 separation state 失败:{error}")
})?;
fn write_separation_state_blocking(path: &Path, bytes: &[u8]) -> Result<(), String> {
if bytes.len() > SEPARATION_STATE_MAX_BYTES {
app_log!(
"ui_separation.error stage=state_write reason=too_large bytes={} max_bytes={}",
@@ -77,6 +66,13 @@ fn write_separation_state_blocking(path: &Path, state: &SeparationState) -> Resu
SEPARATION_STATE_MAX_BYTES
));
}
app_log!(
"ui_separation.state_write.start file={} bytes={}",
path.file_name()
.and_then(|name| name.to_str())
.unwrap_or("<unknown>"),
bytes.len()
);
let parent = path.parent().ok_or_else(|| {
app_log!("ui_separation.error stage=state_write reason=missing_parent");
"separation state 路径缺少父目录".to_string()
@@ -95,16 +91,14 @@ fn write_separation_state_blocking(path: &Path, state: &SeparationState) -> Resu
format!("安装 separation state 失败:{error}")
})?;
app_log!(
"ui_separation.state_write.completed file={} bytes={} trees={} bound={} problematic={}",
"ui_separation.state_write.completed file={} bytes={}",
path.file_name()
.and_then(|name| name.to_str())
.unwrap_or("<unknown>"),
fs::metadata(path)
.map(|metadata| metadata.len())
.unwrap_or(0),
state.trees.len(),
state.bound.len(),
state.problematic_nodes.len()
bytes.len()
);
Ok(())
}
@@ -106,7 +106,7 @@ pub(crate) async fn separate_ui_impl(
image.pixel_size.x.round() as u32,
image.pixel_size.y.round() as u32
);
write_separation_state(state_path.clone(), separation.clone())
write_separation_state(state_path.clone(), &separation)
.await
.map_err(|error| {
app_log!(
@@ -156,7 +156,7 @@ pub(crate) async fn separate_ui_impl(
Ok(value) => value,
Err(error) => {
app_log!("ui_separation.error stage=image_edit tree_index={} batch_index={} error={error}", tree_index, batch_index);
write_separation_state(state_path.clone(), separation.clone()).await?;
write_separation_state(state_path.clone(), &separation).await?;
return Err(error);
}
};
@@ -170,7 +170,7 @@ pub(crate) async fn separate_ui_impl(
Ok(dimensions) => dimensions,
Err(error) => {
app_log!("ui_separation.error stage=processed_image_write tree_index={} batch_index={} error={error}", tree_index, batch_index);
write_separation_state(state_path.clone(), separation.clone()).await?;
write_separation_state(state_path.clone(), &separation).await?;
return Err(error);
}
};
@@ -186,7 +186,7 @@ pub(crate) async fn separate_ui_impl(
Ok(value) => value,
Err(error) => {
app_log!("ui_separation.error stage=visual_binding tree_index={} batch_index={} error={error}", tree_index, batch_index);
write_separation_state(state_path.clone(), separation.clone()).await?;
write_separation_state(state_path.clone(), &separation).await?;
return Err(error);
}
};
@@ -237,7 +237,7 @@ pub(crate) async fn separate_ui_impl(
}
if let Some(error) = cut_error {
app_log!("ui_separation.error stage=cut_batch tree_index={} batch_index={} error={error}", tree_index, batch_index);
write_separation_state(state_path.clone(), separation.clone()).await?;
write_separation_state(state_path.clone(), &separation).await?;
return Err(error);
}
patch::apply_batch_patch(
@@ -247,7 +247,7 @@ pub(crate) async fn separate_ui_impl(
&cut_paths,
processed_dimensions,
)?;
write_separation_state(state_path.clone(), separation.clone()).await?;
write_separation_state(state_path.clone(), &separation).await?;
app_log!(
"ui_separation.batch_completed tree_index={} batch_index={} cuts={} bound={} problematic={} elapsed_ms={}",
tree_index, batch_index, cut_paths.len(), separation.bound.len(), separation.problematic_nodes.len(), batch_started.elapsed().as_millis()
@@ -70,10 +70,8 @@ where
max_retries,
error
);
tokio::time::sleep(std::time::Duration::from_millis(
200 * (attempt as u64 + 1),
))
.await;
tokio::time::sleep(std::time::Duration::from_millis(200 * (attempt as u64 + 1)))
.await;
let serialized = serde_json::to_string(&value)
.map_err(|serialize_error| format!("序列化修复反馈失败:{serialize_error}"))?;
history.push(LlmMessage::system(format!(