core: writer-owned relocation
The worker no longer copies rows when an array cannot grow in place. It reserves a zeroed region, returns the staged descriptor with a commit flag, and queues every other request until the main thread (the only writer) has copied the old bytes and sent commit-rows. Writes that raced the old worker-side copy were lost; lights in the glTF viewer regularly ended up at the origin because of it. Amp-Thread-ID: https://ampcode.com/threads/T-01a0b2dd-db32-712d-99ce-12ce4a86d979 Co-authored-by: Heaust Azure <heaust.azure@gmail.com>
This commit is contained in:
+66
-29
@@ -93,6 +93,8 @@ impl Core {
|
||||
serde_json::to_string(&gpu.as_ref().map(|gpu| &gpu.features[..]).unwrap_or(&[])).unwrap()
|
||||
}
|
||||
|
||||
/// Creates or grows an array. When `commit` is set the rows moved: the caller must copy the old
|
||||
/// bytes into the returned descriptor and send `commit_rows` before any other request.
|
||||
pub fn create_rows(
|
||||
&self,
|
||||
name: String,
|
||||
@@ -100,20 +102,18 @@ impl Core {
|
||||
stride: u32,
|
||||
format: String,
|
||||
) -> Result<String, JsError> {
|
||||
let rows = self
|
||||
let (rows, moved) = self
|
||||
.data
|
||||
.borrow_mut()
|
||||
.create_rows(name, rows, stride, format)
|
||||
.map_err(JsError::new)?;
|
||||
if let Some(gpu) = self.gpu.borrow().as_ref() {
|
||||
self.store
|
||||
.borrow_mut()
|
||||
.refresh_rows(&rows.name, gpu, &self.data.borrow())
|
||||
.map_err(|error| JsError::new(&error))?;
|
||||
if !moved {
|
||||
self.refresh_rows(&rows.name)?;
|
||||
}
|
||||
Ok(serde_json::to_string(&rows).unwrap())
|
||||
Ok(serde_json::json!({ "rows": rows, "commit": moved }).to_string())
|
||||
}
|
||||
|
||||
/// Like `create_rows` for several arrays; `commit` lists the arrays that moved.
|
||||
pub fn create_rows_batch(&self, source: &str) -> Result<String, JsError> {
|
||||
let requests: Vec<RowRequest> =
|
||||
serde_json::from_str(source).map_err(|_| JsError::new("ROWS"))?;
|
||||
@@ -123,25 +123,66 @@ impl Core {
|
||||
let refresh = requests
|
||||
.iter()
|
||||
.any(|request| self.store.borrow().uses_rows(&request.name));
|
||||
let descriptors = {
|
||||
let results = {
|
||||
let mut data = self.data.borrow_mut();
|
||||
requests
|
||||
let results = requests
|
||||
.into_iter()
|
||||
.map(|request| {
|
||||
data.create_rows(request.name, request.rows, request.stride, request.format)
|
||||
.map_err(JsError::new)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
};
|
||||
if refresh {
|
||||
if let Some(gpu) = self.gpu.borrow().as_ref() {
|
||||
self.store
|
||||
.borrow_mut()
|
||||
.refresh(gpu, &self.data.borrow())
|
||||
.map_err(|error| JsError::new(&error))?;
|
||||
.collect::<Result<Vec<_>, _>>();
|
||||
if results.is_err() {
|
||||
data.discard_pending();
|
||||
}
|
||||
results.map_err(JsError::new)?
|
||||
};
|
||||
let commit = results
|
||||
.iter()
|
||||
.filter(|(_, moved)| *moved)
|
||||
.map(|(rows, _)| rows.name.clone())
|
||||
.collect::<Vec<_>>();
|
||||
if refresh && results.iter().any(|(_, moved)| !moved) {
|
||||
self.refresh()?;
|
||||
}
|
||||
Ok(serde_json::to_string(&descriptors).unwrap())
|
||||
let descriptors = results
|
||||
.into_iter()
|
||||
.map(|(rows, _)| rows)
|
||||
.collect::<Vec<_>>();
|
||||
Ok(serde_json::json!({ "rows": descriptors, "commit": commit }).to_string())
|
||||
}
|
||||
|
||||
/// Publishes relocations the caller finished copying, then rebuilds the GPU side once.
|
||||
pub fn commit_rows(&self, source: &str) -> Result<(), JsError> {
|
||||
let names: Vec<String> = serde_json::from_str(source).map_err(|_| JsError::new("ROWS"))?;
|
||||
self.data
|
||||
.borrow_mut()
|
||||
.commit(&names)
|
||||
.map_err(JsError::new)?;
|
||||
if names.iter().any(|name| self.store.borrow().uses_rows(name)) {
|
||||
self.refresh()?;
|
||||
}
|
||||
self.data.borrow_mut().mark_dirty();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn refresh_rows(&self, name: &str) -> Result<(), JsError> {
|
||||
if let Some(gpu) = self.gpu.borrow().as_ref() {
|
||||
self.store
|
||||
.borrow_mut()
|
||||
.refresh_rows(name, gpu, &self.data.borrow())
|
||||
.map_err(|error| JsError::new(&error))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn refresh(&self) -> Result<(), JsError> {
|
||||
if let Some(gpu) = self.gpu.borrow().as_ref() {
|
||||
self.store
|
||||
.borrow_mut()
|
||||
.refresh(gpu, &self.data.borrow())
|
||||
.map_err(|error| JsError::new(&error))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn delete_rows(&self, name: String) -> Result<(), JsError> {
|
||||
@@ -154,8 +195,9 @@ impl Core {
|
||||
.map_err(JsError::new)
|
||||
}
|
||||
|
||||
/// Reserves rows; `commit` is set when the array moved, exactly like `create_rows`.
|
||||
pub fn allocate_object(&self, name: &str, count: u32) -> Result<String, JsError> {
|
||||
let (id, grew) = self
|
||||
let (id, grew, moved) = self
|
||||
.data
|
||||
.borrow_mut()
|
||||
.allocate_object(name, count)
|
||||
@@ -163,18 +205,13 @@ impl Core {
|
||||
let rows = self
|
||||
.data
|
||||
.borrow()
|
||||
.rows(name)
|
||||
.staged(name)
|
||||
.ok_or_else(|| JsError::new("ROWS_UNKNOWN"))?
|
||||
.clone();
|
||||
if grew {
|
||||
if let Some(gpu) = self.gpu.borrow().as_ref() {
|
||||
self.store
|
||||
.borrow_mut()
|
||||
.refresh_rows(name, gpu, &self.data.borrow())
|
||||
.map_err(|error| JsError::new(&error))?;
|
||||
}
|
||||
if grew && !moved {
|
||||
self.refresh_rows(name)?;
|
||||
}
|
||||
Ok(serde_json::json!({ "id": id, "rows": rows }).to_string())
|
||||
Ok(serde_json::json!({ "id": id, "rows": rows, "commit": moved }).to_string())
|
||||
}
|
||||
|
||||
pub fn delete_object(&self, name: &str, id: u32) -> Result<(), JsError> {
|
||||
|
||||
Reference in New Issue
Block a user