Fix blank drawings, drop parse-time animation cost, add render diagnostics

Accuracy / completeness
- docViews: split parsed scene into model space / paper space / free-standing
  blocks (geometry hidden in uninserted blocks no longer shows as a blank
  canvas); auto-select the first block view when model space is empty
- viewer: view switcher panel listing every detected view with expanded
  entity counts; paper space is now parsed (includePaperSpace) and kept apart
- SHX: missing .shx references are surfaced with an import control
  (addReferenceFile), linetypes/text redraw afterwards
- unsupported/proxy entities: markers on canvas + diagnostics block showing
  proxy / wipeout counts and missing SHX names

Speed
- the loading overlay used an infinite CSS spinner + backdrop blur while the
  worker parsed; on software rendering that starved the parse thread
  (TELECOM 18MB: 71s -> 30s wall clock, normalize 33s -> 14s). Replaced with a
  determinate progress bar fed by loader progress events
- transferInputBuffer for worker loads, maxInsertDepth 32, longer worker timeout

Tooling
- scripts/probe-dwg.mjs / probe-app.mjs: headless render + end-to-end probes
  (entity/layer/block/layout/proxy stats, timings, screenshots)
- tools/render-probe.html, scripts/profile-worker.mjs
- version 0.6.0 (versionCode 8)
This commit is contained in:
2026-10-26 20:28:30 +08:00
parent f70913481e
commit be34c630bb
14 changed files with 1290 additions and 24 deletions
+49
View File
@@ -0,0 +1,49 @@
// 在 Node 里直接跑 DWG worker 的解析流程(带 CPU profile),用于定位热点。
// 用法:node --cpu-prof --cpu-prof-dir=/tmp/prof tools/worker-node-run.mjs <dwg> <静态资源根 URL>
import fs from 'node:fs'
import path from 'node:path'
import { pathToFileURL } from 'node:url'
const root = process.cwd()
const file = process.argv[2]
const wasmBase = process.argv[3] || 'http://127.0.0.1:8123/wasm/'
if (!file) throw new Error('需要 DWG 路径')
const messages = []
globalThis.self = {
postMessage(msg) {
messages.push(msg)
if (msg.type === 'result') {
const doc = msg.result?.document
console.log('结果: 实体', doc?.entities?.length, '耗时', Math.round(msg.result.elapsedMs), 'ms')
for (const m of messages.filter((x) => x.type === 'progress')) {
console.log(' progress', m.progress.phase, '|', m.progress.message)
}
process.exit(0)
}
if (msg.type === 'error') {
console.error('worker 错误:', msg.error)
process.exit(1)
}
},
addEventListener() {},
removeEventListener() {}
}
globalThis.performance = performance
globalThis.window = globalThis // 让 Emscripten 走 Web 分支(用 fetch 而不是 node:module)
await import(pathToFileURL(path.join(root, 'public/wasm/dwg-worker.js')).href)
const bytes = fs.readFileSync(file)
const buf = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength)
const started = Date.now()
await globalThis.self.onmessage({
data: {
requestId: 1,
type: 'load',
bytes: buf,
fileName: path.basename(file),
options: { wasmPath: wasmBase }
}
})
console.log('总耗时', Date.now() - started, 'ms')