Skip to content

Commit d412335

Browse files
authored
perf: optimize workspace responsiveness via SVG caching and coalesced bounds checking (#6956)
* perf: optimize workspace responsiveness via SVG caching and coalesced bounds checking This commit implements several performance optimizations: - Implements memoization for base64Encode in musicutils.js to reduce SVG processing overhead. - Adds an icon cache in palette.js to speed up category switching and search. - Coalesces checkBounds calls in blocks.js during dragging using requestAnimationFrame. - Removes redundant setTimeout throttle in activity.js for lower render latency. * fix: remove duplicate escapeHTML method in ReflectionMatrix
1 parent 2955afa commit d412335

5 files changed

Lines changed: 27 additions & 39 deletions

File tree

js/activity.js

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5010,42 +5010,8 @@ class Activity {
50105010
let lastRefreshReport = performance.now();
50115011

50125012
this.refreshCanvas = () => {
5013-
if (this.blockRefreshCanvas) {
5014-
return;
5015-
}
5016-
5017-
const start = window.__ENABLE_REFRESH_PROFILING__ ? performance.now() : 0;
5018-
5019-
this.blockRefreshCanvas = true;
50205013
this.stageDirty = true;
50215014
this.update = true;
5022-
5023-
const that = this;
5024-
setTimeout(() => {
5025-
that.blockRefreshCanvas = false;
5026-
that.stageDirty = true;
5027-
5028-
if (window.__ENABLE_REFRESH_PROFILING__) {
5029-
const duration = performance.now() - start;
5030-
refreshCount++;
5031-
totalRefreshTime += duration;
5032-
maxRefreshTime = Math.max(maxRefreshTime, duration);
5033-
5034-
if (refreshCount % 25 === 0) {
5035-
const now = performance.now();
5036-
const cps = (25 / (now - lastRefreshReport)) * 1000;
5037-
console.log(
5038-
`refreshCanvas | Avg: ${(totalRefreshTime / refreshCount).toFixed(
5039-
2
5040-
)}ms | Max: ${maxRefreshTime.toFixed(2)}ms | Rate: ${cps.toFixed(
5041-
1
5042-
)} calls/sec`
5043-
);
5044-
maxRefreshTime = 0;
5045-
lastRefreshReport = now;
5046-
}
5047-
}
5048-
}, 5);
50495015
};
50505016

50515017
/*

js/blocks.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,7 @@ class Blocks {
10181018

10191019
if (this._deferCheckBoundsCount === 0 && this._checkBoundsPending) {
10201020
this._checkBoundsPending = false;
1021-
this.checkBounds();
1021+
this.scheduleCheckBounds();
10221022
}
10231023
};
10241024

@@ -2539,7 +2539,7 @@ class Blocks {
25392539
if (this._deferCheckBoundsCount > 0) {
25402540
this._checkBoundsPending = true;
25412541
} else {
2542-
this.checkBounds();
2542+
this.scheduleCheckBounds();
25432543
}
25442544
} else {
25452545
console.debug("No container yet for block " + myBlock.name);
@@ -2565,7 +2565,7 @@ class Blocks {
25652565
if (this._deferCheckBoundsCount > 0) {
25662566
this._checkBoundsPending = true;
25672567
} else {
2568-
this.checkBounds();
2568+
this.scheduleCheckBounds();
25692569
}
25702570
} else {
25712571
console.debug("No container yet for block " + myBlock.name);

js/palette.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
// License along with this library; if not, write to the Free Software
1010
// Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
1111

12+
const _paletteIconCache = new Map();
13+
1214
/* global
1315
docById, LEADING, DEFAULTPALETTE, MULTIPALETTES, platformColor,
1416
PALETTEICONS, MULTIPALETTEICONS, SKIPPALETTES, toTitleCase,
@@ -45,8 +47,20 @@ const paletteBlockButtonPush = (blocks, name, arg) => {
4547
// loadPaletteMenuItemHandler is the event handler for the palette menu.
4648

4749
const makePaletteIcons = (data, width, height) => {
50+
const key = `${data}_${width}_${height}`;
51+
let src;
52+
if (_paletteIconCache.has(key)) {
53+
src = _paletteIconCache.get(key);
54+
} else {
55+
src = "data:image/svg+xml;base64," + window.btoa(base64Encode(data));
56+
if (_paletteIconCache.size > 500) {
57+
_paletteIconCache.clear();
58+
}
59+
_paletteIconCache.set(key, src);
60+
}
61+
4862
const img = new Image();
49-
img.src = "data:image/svg+xml;base64," + window.btoa(base64Encode(data));
63+
img.src = src;
5064
if (width) img.width = width;
5165
if (height) img.height = height;
5266
return img;

js/utils/musicutils.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
_, last, DRUMNAMES, NOISENAMES, VOICENAMES, INVALIDPITCH, CUSTOMSAMPLES
1616
*/
1717

18+
const _b64Cache = new Map();
19+
1820
/*
1921
Global Locations
2022
js/utils/utils.js
@@ -4133,9 +4135,16 @@ const GetNotesForInterval = tur => {
41334135
* @returns {string} - The Base64 encoded string.
41344136
*/
41354137
function base64Encode(str) {
4138+
if (_b64Cache.has(str)) {
4139+
return _b64Cache.get(str);
4140+
}
41364141
const encoder = new TextEncoder();
41374142
const uint8Array = encoder.encode(str);
41384143
const binaryString = String.fromCharCode(...uint8Array);
4144+
if (_b64Cache.size > 1000) {
4145+
_b64Cache.clear();
4146+
}
4147+
_b64Cache.set(str, binaryString);
41394148
return binaryString;
41404149
}
41414150

js/widgets/reflection.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,6 @@ class ReflectionMatrix {
737737
document.body.removeChild(a);
738738
URL.revokeObjectURL(url);
739739
}
740-
741740
/**
742741
* Sanitizes HTML content using DOMParser to prevent XSS.
743742
* Removes unsafe attributes and ensures links are safe.

0 commit comments

Comments
 (0)