Skip to content

Commit 28e1519

Browse files
committed
Granular rect memory
1 parent ea0151d commit 28e1519

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed

packages/core/pluggableElementTypes/renderers/BoxRendererType.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ export default class BoxRendererType extends FeatureRendererType {
6767
const key = getLayoutId(props)
6868
if (!this.layoutSessions[key]) {
6969
this.layoutSessions[key] = this.createLayoutSession(props)
70+
// console.log(
71+
// `[BoxRendererType.getWorkerSession] created new session: ${key}, ` +
72+
// `total sessions: ${Object.keys(this.layoutSessions).length}`,
73+
// )
7074
}
7175
return this.layoutSessions[key].update(props)
7276
}
@@ -84,8 +88,17 @@ export default class BoxRendererType extends FeatureRendererType {
8488
const session = this.layoutSessions[key]
8589
if (session) {
8690
const region = args.regions[0]!
91+
// console.log(
92+
// `[BoxRendererType.freeResources] key: ${key}, ` +
93+
// `region: ${region.refName}:${region.start}-${region.end}`,
94+
// )
8795
session.layout.discardRange(region.refName, region.start, region.end)
8896
}
97+
// else {
98+
// console.log(
99+
// `[BoxRendererType.freeResources] no session found for key: ${key}`,
100+
// )
101+
// }
89102
}
90103

91104
async freeResourcesInClient(rpcManager: RpcManager, args: RenderArgs) {

packages/core/util/layouts/GranularRectLayout.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,51 @@ test('discards regions', () => {
9696
expect(l.bitmap[0].intervals.length).toBe(0)
9797
})
9898

99+
test('discardRange removes rectangles from the Map to prevent memory leaks', () => {
100+
const l = new Layout({ pitchX: 10, pitchY: 4 })
101+
102+
// Add features in the range 0-100000
103+
for (let i = 0; i < 10; i++) {
104+
l.addRect(`feature-${i}`, i * 10000, i * 10000 + 5000, 1)
105+
}
106+
107+
// @ts-expect-error accessing private property for testing
108+
expect(l.rectangles.size).toBe(10)
109+
110+
// Discard the entire range - all rectangles should be removed
111+
l.discardRange(0, 100000)
112+
113+
// @ts-expect-error accessing private property for testing
114+
expect(l.rectangles.size).toBe(0)
115+
})
116+
117+
test('discardRange only removes rectangles fully within range', () => {
118+
const l = new Layout({ pitchX: 10, pitchY: 4 })
119+
120+
// Add features at different positions
121+
l.addRect('inside', 5000, 6000, 1) // fully inside discard range
122+
l.addRect('outside-left', 0, 1000, 1) // fully outside (left)
123+
l.addRect('outside-right', 9000, 10000, 1) // fully outside (right)
124+
l.addRect('spanning', 2000, 8000, 1) // spans the discard range
125+
126+
// @ts-expect-error accessing private property for testing
127+
expect(l.rectangles.size).toBe(4)
128+
129+
// Discard range 3000-7000
130+
l.discardRange(3000, 7000)
131+
132+
// @ts-expect-error accessing private property for testing
133+
expect(l.rectangles.size).toBe(3)
134+
// @ts-expect-error accessing private property for testing
135+
expect(l.rectangles.has('inside')).toBe(false)
136+
// @ts-expect-error accessing private property for testing
137+
expect(l.rectangles.has('outside-left')).toBe(true)
138+
// @ts-expect-error accessing private property for testing
139+
expect(l.rectangles.has('outside-right')).toBe(true)
140+
// @ts-expect-error accessing private property for testing
141+
expect(l.rectangles.has('spanning')).toBe(true)
142+
})
143+
99144
// see issue #486
100145
test('tests that adding +/- pitchX fixes resolution causing errors', () => {
101146
const l = new Layout({ pitchX: 91.21851599727707, pitchY: 3 })

packages/core/util/layouts/GranularRectLayout.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,23 @@ export default class GranularRectLayout<T> implements BaseLayout<T> {
372372
this.addRectToBitmap(rectangle)
373373
this.rectangles.set(id, rectangle)
374374
this.pTotalHeight = Math.max(this.pTotalHeight || 0, top + pHeight)
375+
376+
// // Log every 1000 rectangles to track memory growth
377+
// if (this.rectangles.size % 1000 === 0) {
378+
// const memoryMB =
379+
// typeof performance !== 'undefined' &&
380+
// // @ts-expect-error
381+
// performance.memory !== undefined
382+
// ? // @ts-expect-error
383+
// (performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(2)
384+
// : 'N/A'
385+
// console.log(
386+
// `[GranularRectLayout.addRect] rectangles: ${this.rectangles.size}, ` +
387+
// `bitmap rows: ${this.bitmap.filter(Boolean).length}, ` +
388+
// `heap: ${memoryMB} MB`,
389+
// )
390+
// }
391+
375392
return top * this.pitchY
376393
}
377394

@@ -439,9 +456,38 @@ export default class GranularRectLayout<T> implements BaseLayout<T> {
439456
const pLeft = Math.trunc(left / this.pitchX)
440457
const pRight = Math.trunc(right / this.pitchX)
441458
const { bitmap } = this
459+
460+
// const rectsBefore = this.rectangles.size
461+
// const bitmapRowCount = bitmap.filter(Boolean).length
462+
442463
for (const row of bitmap) {
443464
row.discardRange(pLeft, pRight)
444465
}
466+
467+
// Also remove rectangles that fall completely within the discarded range
468+
// let deletedCount = 0
469+
for (const [id, rect] of this.rectangles) {
470+
if (rect.l >= pLeft && rect.r <= pRight) {
471+
this.rectangles.delete(id)
472+
// deletedCount++
473+
}
474+
}
475+
476+
// const rectsAfter = this.rectangles.size
477+
// const memoryMB =
478+
// typeof performance !== 'undefined' &&
479+
// // @ts-expect-error
480+
// performance.memory !== undefined
481+
// ? // @ts-expect-error
482+
// (performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(2)
483+
// : 'N/A'
484+
485+
// console.log(
486+
// `[GranularRectLayout.discardRange] range: ${left}-${right}, ` +
487+
// `rectangles: ${rectsBefore} → ${rectsAfter} (deleted ${deletedCount}), ` +
488+
// `bitmap rows: ${bitmapRowCount}, ` +
489+
// `heap: ${memoryMB} MB`,
490+
// )
445491
}
446492

447493
hasSeen(id: string) {

0 commit comments

Comments
 (0)