mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-29 14:53:44 +08:00
perf(性能提升): 优化字体并发与复杂页渲染
This commit is contained in:
1 parent
15a532a1ca
commit
04d7d2d672
3 files changed
+52
-20
No files matched your search
+43
-18
@@ -64,16 +64,16 @@ class BrowserScheduler {
|
|||||||
if (data.id == null || data.type) return;
|
if (data.id == null || data.type) return;
|
||||||
const key = requestKey(worker, data);
|
const key = requestKey(worker, data);
|
||||||
const started = worker.requests.get(key);
|
const started = worker.requests.get(key);
|
||||||
|
const elapsed = data.elapsed ?? (started === undefined ? 0 : performance.now() - started);
|
||||||
const operation = worker.operations.get(key), owner = this.owner(worker, data);
|
const operation = worker.operations.get(key), owner = this.owner(worker, data);
|
||||||
if (started !== undefined && data.ok && owner) {
|
if (worker.primary && started !== undefined && data.ok && owner) {
|
||||||
const elapsed = performance.now() - started;
|
|
||||||
if (operation?.name === "ofdgoOpen") owner.openTime = elapsed;
|
if (operation?.name === "ofdgoOpen") owner.openTime = elapsed;
|
||||||
if (operation?.name === "ofdgoRenderPage") owner.pageTime = owner.pageTime ? owner.pageTime * 0.75 + elapsed * 0.25 : elapsed;
|
if (operation?.name === "ofdgoRenderPage") owner.pageTime = owner.pageTime ? owner.pageTime * 0.75 + elapsed * 0.25 : elapsed;
|
||||||
}
|
}
|
||||||
worker.requests.delete(key);
|
worker.requests.delete(key);
|
||||||
worker.operations.delete(key);
|
worker.operations.delete(key);
|
||||||
if (this.memory > this.budget) this.limit = Math.max(1, this.limit - 1);
|
if (this.memory > this.budget) this.limit = Math.max(1, this.limit - 1);
|
||||||
else if (started !== undefined && data.ok && performance.now() - started > 100 && this.memory + Math.max(128 << 20, worker.memory) < this.budget) this.limit = Math.min(this.capacity, this.limit + 1);
|
else if (started !== undefined && data.ok && elapsed > 100 && this.memory + Math.max(128 << 20, worker.memory) < this.budget) this.limit = Math.min(this.capacity, this.limit + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
drain() {
|
drain() {
|
||||||
@@ -300,8 +300,9 @@ class BrowserEngine {
|
|||||||
const data = event.data;
|
const data = event.data;
|
||||||
if (data.memory) this.memory = data.memory;
|
if (data.memory) this.memory = data.memory;
|
||||||
if (this.source) this.parallelism = engineScheduler.limit;
|
if (this.source) this.parallelism = engineScheduler.limit;
|
||||||
if (data.id != null && !data.type) this.requests.delete(data.id);
|
const rendered = data.id != null && !data.type && this.requests.delete(data.id);
|
||||||
this.onmessage?.(event);
|
this.onmessage?.(event);
|
||||||
|
if (rendered && data.ok) this.dispatchPages();
|
||||||
};
|
};
|
||||||
this.primary.onerror = event => this.onerror?.(event);
|
this.primary.onerror = event => this.onerror?.(event);
|
||||||
this.primary.onmessageerror = event => this.onmessageerror?.(event);
|
this.primary.onmessageerror = event => this.onmessageerror?.(event);
|
||||||
@@ -407,6 +408,36 @@ class BrowserEngine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pageSlot(data) {
|
||||||
|
const preferred = [...this.slots].find(slot => !slot.busy && slot.pages.has(data.args[0]));
|
||||||
|
const pending = [...this.requests.values()].filter(slot => slot === null).length - Number(this.requests.has(data.id));
|
||||||
|
if (!preferred && pending <= 0) return null;
|
||||||
|
let slot = preferred || [...this.slots].filter(slot => !slot.busy).sort((a,b) => Math.abs(a.lastPage-data.args[0])-Math.abs(b.lastPage-data.args[0]))[0];
|
||||||
|
const worthwhile = (this.primary.pageTime || 0) * pending >= Math.max(50, this.startupTime || this.primary.openTime || 0);
|
||||||
|
if (!slot && worthwhile && this.foreground && !this.pagesPaused && this.slots.size < this.parallelism - 1
|
||||||
|
&& engineScheduler.memory + Math.max(128 << 20, this.memory) <= this.budget) slot = this.createSlot(this.source);
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchPages() {
|
||||||
|
if (this.closed || !this.source || !this.foreground || this.pagesPaused) return;
|
||||||
|
const draining = engineScheduler.draining;
|
||||||
|
engineScheduler.draining = true;
|
||||||
|
try {
|
||||||
|
const queued = engineScheduler.queue.filter(task => task.worker === this.primary.runtime && task.data.engineID === this.engineID).sort((a,b) => a.order-b.order);
|
||||||
|
for (const task of queued) {
|
||||||
|
if (task.data.name !== "ofdgoRenderPage") break;
|
||||||
|
if (this.requests.get(task.data.id) !== null) continue;
|
||||||
|
let slot;
|
||||||
|
try { slot = this.pageSlot(task.data); } catch { break; }
|
||||||
|
if (!slot) break;
|
||||||
|
engineScheduler.queue.splice(engineScheduler.queue.indexOf(task), 1);
|
||||||
|
this.primary.requests.delete(task.data.id);
|
||||||
|
this.renderSlot(slot, task.data, task.transfer);
|
||||||
|
}
|
||||||
|
} finally { engineScheduler.draining = draining; }
|
||||||
|
}
|
||||||
|
|
||||||
postMessage(data, transfer) {
|
postMessage(data, transfer) {
|
||||||
if (this.closed) return;
|
if (this.closed) return;
|
||||||
if (!data.type && data.preload && this.pagesPaused) {
|
if (!data.type && data.preload && this.pagesPaused) {
|
||||||
@@ -415,7 +446,7 @@ class BrowserEngine {
|
|||||||
}
|
}
|
||||||
if (data.type === "cancel") {
|
if (data.type === "cancel") {
|
||||||
const slot = this.requests.get(data.id);
|
const slot = this.requests.get(data.id);
|
||||||
if (slot) this.closeSlot(slot);
|
if (slot && slot !== this.primary) this.closeSlot(slot);
|
||||||
else this.primary.postMessage(data);
|
else this.primary.postMessage(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -430,23 +461,16 @@ class BrowserEngine {
|
|||||||
"ofdgoPreparePrint", "ofdgoLayoutPrint", "ofdgoPrintSide", "ofdgoPrintPDF", "ofdgoDiscardPrint",
|
"ofdgoPreparePrint", "ofdgoLayoutPrint", "ofdgoPrintSide", "ofdgoPrintPDF", "ofdgoDiscardPrint",
|
||||||
"ofdgoExportAttachment", "ofdgoExportResource", "ofdgoExportMedia", "ofdgoExportVersion"
|
"ofdgoExportAttachment", "ofdgoExportResource", "ofdgoExportMedia", "ofdgoExportVersion"
|
||||||
].includes(data.name)) this.invalidatePages();
|
].includes(data.name)) this.invalidatePages();
|
||||||
const preferred = data.name === "ofdgoRenderPage" ? [...this.slots].find(slot => !slot.busy && slot.pages.has(data.args[0])) : null;
|
const slot = data.name === "ofdgoRenderPage" && this.source ? this.pageSlot(data) : null;
|
||||||
if (data.name !== "ofdgoRenderPage" || !this.source || !preferred && ![...this.requests.values()].includes(null)) {
|
if (!slot) {
|
||||||
if (data.name === "ofdgoRenderPage") this.requests.set(data.id, null);
|
if (data.name === "ofdgoRenderPage") this.requests.set(data.id, null);
|
||||||
this.primary.postMessage(data, transfer);
|
this.primary.postMessage(data, transfer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let slot = preferred || [...this.slots].filter(slot => !slot.busy).sort((a,b) => Math.abs(a.lastPage-data.args[0])-Math.abs(b.lastPage-data.args[0]))[0];
|
this.renderSlot(slot, data, transfer);
|
||||||
const memory = engineScheduler.memory;
|
|
||||||
const pending = [...this.requests.values()].filter(slot => slot === null).length;
|
|
||||||
const worthwhile = (this.primary.pageTime || 0) * pending >= Math.max(50, this.startupTime || this.primary.openTime || 0);
|
|
||||||
if (!slot && worthwhile && this.foreground && !this.pagesPaused
|
|
||||||
&& this.slots.size < this.parallelism - 1 && memory + Math.max(128 << 20, this.memory) <= this.budget) slot = this.createSlot(this.source);
|
|
||||||
if (!slot) {
|
|
||||||
this.requests.set(data.id, null);
|
|
||||||
this.primary.postMessage(data, transfer);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderSlot(slot, data, transfer) {
|
||||||
slot.busy = true;
|
slot.busy = true;
|
||||||
this.requests.set(data.id, slot);
|
this.requests.set(data.id, slot);
|
||||||
const revision = this.revision;
|
const revision = this.revision;
|
||||||
@@ -462,7 +486,7 @@ class BrowserEngine {
|
|||||||
if (!this.closed && revision === this.revision && error.name !== "AbortError") {
|
if (!this.closed && revision === this.revision && error.name !== "AbortError") {
|
||||||
this.closeSlot(slot, error);
|
this.closeSlot(slot, error);
|
||||||
this.parallelism = Math.max(1, this.slots.size + 1);
|
this.parallelism = Math.max(1, this.slots.size + 1);
|
||||||
this.requests.set(data.id, null);
|
this.requests.set(data.id, this.primary);
|
||||||
fallback = true;
|
fallback = true;
|
||||||
this.primary.postMessage(data, transfer);
|
this.primary.postMessage(data, transfer);
|
||||||
return;
|
return;
|
||||||
@@ -474,6 +498,7 @@ class BrowserEngine {
|
|||||||
slot.worker.reservation = 0;
|
slot.worker.reservation = 0;
|
||||||
const memory = engineScheduler.memory;
|
const memory = engineScheduler.memory;
|
||||||
if (memory > this.budget) { this.closeSlot(slot); this.parallelism = Math.max(1, this.slots.size + 1); }
|
if (memory > this.budget) { this.closeSlot(slot); this.parallelism = Math.max(1, this.slots.size + 1); }
|
||||||
|
this.dispatchPages();
|
||||||
engineScheduler.drain();
|
engineScheduler.drain();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -535,6 +535,10 @@ export function createPageView(context) {
|
|||||||
const results = await Promise.allSettled(fonts.slice(offset, offset + 4).map(async font => {
|
const results = await Promise.allSettled(fonts.slice(offset, offset + 4).map(async font => {
|
||||||
if (state.svgFonts.has(font.name)) return;
|
if (state.svgFonts.has(font.name)) return;
|
||||||
const key = `${openSeq}:${loadSeq}:${font.name}`;
|
const key = `${openSeq}:${loadSeq}:${font.name}`;
|
||||||
|
while (current() && !state.svgFonts.has(font.name) && !state.svgFontLoads.has(key) && state.svgFontLoads.size >= 4) {
|
||||||
|
await Promise.race([...state.svgFontLoads.values()].map(loading => loading.catch(() => {})));
|
||||||
|
}
|
||||||
|
if (!current() || state.svgFonts.has(font.name)) return;
|
||||||
let loading = state.svgFontLoads.get(key);
|
let loading = state.svgFontLoads.get(key);
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
loading = pageView.loadSVGFont(font, openSeq, loadSeq);
|
loading = pageView.loadSVGFont(font, openSeq, loadSeq);
|
||||||
@@ -615,8 +619,9 @@ export function createPageView(context) {
|
|||||||
if (task.shell && pageView.pageShell(task.index) === task.shell && !task.shell.classList.contains("rendered")
|
if (task.shell && pageView.pageShell(task.index) === task.shell && !task.shell.classList.contains("rendered")
|
||||||
&& (!state.pageObserver || state.visiblePages.has(task.index) || task.index === state.pageIndex)) {
|
&& (!state.pageObserver || state.visiblePages.has(task.index) || task.index === state.pageIndex)) {
|
||||||
task.text ||= pageView.createTextLayerParts(task.page.text);
|
task.text ||= pageView.createTextLayerParts(task.page.text);
|
||||||
const text = task.text.next();
|
let text;
|
||||||
if (!text.done) continue;
|
do { text = task.text.next(); } while (!text.done && performance.now() - started < 6);
|
||||||
|
if (!text.done) break;
|
||||||
pageView.mountPageSVG(task.index, task.page, task.openSeq, text.value);
|
pageView.mountPageSVG(task.index, task.page, task.openSeq, text.value);
|
||||||
}
|
}
|
||||||
if (task.thumb && context.el.pageList.querySelector(`[data-page-index="${task.index}"] .thumb-paper`) === task.thumb) pageView.updateThumbnail(task.index, task.openSeq);
|
if (task.thumb && context.el.pageList.querySelector(`[data-page-index="${task.index}"] .thumb-paper`) === task.thumb) pageView.updateThumbnail(task.index, task.openSeq);
|
||||||
|
|||||||
@@ -337,9 +337,11 @@ async function handleMessage(engine, { id, name, args }) {
|
|||||||
if (!saving && args.some(value => value?.mode !== undefined)) args.push(done => finish(done));
|
if (!saving && args.some(value => value?.mode !== undefined)) args.push(done => finish(done));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const started = name === "ofdgoOpen" || name === "ofdgoRenderPage" ? performance.now() : null;
|
||||||
const payload = await globalThis.ofdgoEngineCall(engine.id, name, ...args);
|
const payload = await globalThis.ofdgoEngineCall(engine.id, name, ...args);
|
||||||
signal?.throwIfAborted();
|
signal?.throwIfAborted();
|
||||||
const result = typeof payload === "string" ? JSON.parse(payload) : payload;
|
const result = typeof payload === "string" ? JSON.parse(payload) : payload;
|
||||||
|
if (started !== null && result.ok) result.elapsed = performance.now() - started;
|
||||||
if (cancellable && !importing && !converting && !resources && result.ok) {
|
if (cancellable && !importing && !converting && !resources && result.ok) {
|
||||||
result.data.size = size;
|
result.data.size = size;
|
||||||
if (name !== "ofdgoExportMedia") postMessage({ id, type: "export", stage: "save" });
|
if (name !== "ofdgoExportMedia") postMessage({ id, type: "export", stage: "save" });
|
||||||
|
|||||||
Reference in new issue
Block a user