@volter/blender-engine 0.1.45 → 0.1.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/runtime.ts +6 -0
- package/browser/worker.ts +22 -3
- package/package.json +1 -1
package/browser/runtime.ts
CHANGED
|
@@ -225,6 +225,12 @@ export class BlenderRuntime {
|
|
|
225
225
|
return this.#document;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
/** Blender's last lines of output (the worker's log, newest last): what a status read shows,
|
|
229
|
+
* including each document save's size and phase timings (`@@VOLTER-DOCUMENT`). */
|
|
230
|
+
get lastOutput(): readonly string[] {
|
|
231
|
+
return [...this.#lastLines];
|
|
232
|
+
}
|
|
233
|
+
|
|
228
234
|
/** Idempotent: the first call boots Blender at `project`'s
|
|
229
235
|
* absolute path; later calls await it (a different path is refused).
|
|
230
236
|
*
|
package/browser/worker.ts
CHANGED
|
@@ -198,9 +198,19 @@ function setDocumentDirty(dirty: boolean): void {
|
|
|
198
198
|
async function saveDocument(): Promise<void> {
|
|
199
199
|
if (!engine || documentPath === null) return;
|
|
200
200
|
const relative = documentPath;
|
|
201
|
+
// Each phase's milliseconds, reported with the save: on a large document the save is the
|
|
202
|
+
// edit's cost, and where it goes differs by host (a tab-served project runs the server here).
|
|
203
|
+
const ms: Record<string, number> = {};
|
|
204
|
+
let mark = performance.now();
|
|
205
|
+
const lap = (phase: string) => {
|
|
206
|
+
const now = performance.now();
|
|
207
|
+
ms[phase] = Math.round(now - mark);
|
|
208
|
+
mark = now;
|
|
209
|
+
};
|
|
201
210
|
let answer: { saved?: boolean; path?: string; size?: number };
|
|
202
211
|
try {
|
|
203
212
|
answer = (await engine.request({ op: 'save-document' })) as typeof answer;
|
|
213
|
+
lap('save');
|
|
204
214
|
} catch (error) {
|
|
205
215
|
// A document that cannot be written is the session's work at risk, so it
|
|
206
216
|
// is a named condition in the editor's console, not a debug line.
|
|
@@ -211,6 +221,7 @@ async function saveDocument(): Promise<void> {
|
|
|
211
221
|
let bytes: Uint8Array;
|
|
212
222
|
try {
|
|
213
223
|
bytes = await engine.files.readFile(answer.path);
|
|
224
|
+
lap('read');
|
|
214
225
|
} catch (error) {
|
|
215
226
|
throw new Error(`The Blender document ${relative} could not be read back out of the engine: ${describeThrown(error)}`);
|
|
216
227
|
}
|
|
@@ -222,6 +233,7 @@ async function saveDocument(): Promise<void> {
|
|
|
222
233
|
let sent = 0;
|
|
223
234
|
try {
|
|
224
235
|
const chunks = await documentChunks(bytes);
|
|
236
|
+
lap('chunk');
|
|
225
237
|
const query = `path=${encodeURIComponent(relative)}`;
|
|
226
238
|
const manifest = JSON.stringify({ chunks: chunks.map(({ hash, start, end }) => [hash, end - start]) });
|
|
227
239
|
// The manifest asks for what the server lacks; after those are sent it commits. A second
|
|
@@ -232,9 +244,15 @@ async function saveDocument(): Promise<void> {
|
|
|
232
244
|
headers: { 'content-type': 'application/json' },
|
|
233
245
|
body: manifest,
|
|
234
246
|
});
|
|
235
|
-
const said = (await posted.json().catch(() => null)) as
|
|
247
|
+
const said = (await posted.json().catch(() => null)) as
|
|
248
|
+
| { ok?: boolean; missing?: string[]; error?: string; ms?: Record<string, number> }
|
|
249
|
+
| null;
|
|
250
|
+
lap(`manifest${round}`);
|
|
236
251
|
if (!posted.ok) throw new Error(`HTTP ${posted.status} ${said?.error ?? ''}`);
|
|
237
|
-
if (said?.ok)
|
|
252
|
+
if (said?.ok) {
|
|
253
|
+
for (const [phase, value] of Object.entries(said.ms ?? {})) ms[`server.${phase}`] = value;
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
238
256
|
if (round === 2 || !Array.isArray(said?.missing)) throw new Error(`the server still lacks ${said?.missing?.length ?? '?'} chunks`);
|
|
239
257
|
const wanted = new Set(said.missing);
|
|
240
258
|
for (const chunk of chunks) {
|
|
@@ -247,12 +265,13 @@ async function saveDocument(): Promise<void> {
|
|
|
247
265
|
if (!part.ok) throw new Error(`HTTP ${part.status} ${await part.text().catch(() => '')}`);
|
|
248
266
|
sent += chunk.end - chunk.start;
|
|
249
267
|
}
|
|
268
|
+
lap(`send${round}`);
|
|
250
269
|
}
|
|
251
270
|
} catch (error) {
|
|
252
271
|
throw new Error(`The Blender document ${relative} was not written to the project: ${describeThrown(error)}`);
|
|
253
272
|
}
|
|
254
273
|
setDocumentDirty(false);
|
|
255
|
-
log('log', `@@VOLTER-DOCUMENT ${JSON.stringify({ path: relative, bytes: bytes.length, sent })}`);
|
|
274
|
+
log('log', `@@VOLTER-DOCUMENT ${JSON.stringify({ path: relative, bytes: bytes.length, sent, ms })}`);
|
|
256
275
|
}
|
|
257
276
|
|
|
258
277
|
async function startBlender(project: string, document?: string): Promise<unknown> {
|