Conversation
- CacheStoreGetService: get時にtrash_storeからもエントリを除外し、 removeTimer後の参照で削除予定がスキップされる問題を解消 - DisplayObjectContainerGenerateRenderQueueUseCase: cacheAsBitmap更新時の $removeIds.push漏れを修正し、Worker側にもキャッシュ削除を伝搬 - FillTexturePool: バケット上限32/総数上限256を導入し、超過時はdestroy することでGPUメモリの単調増加を防止 - 各変更に対するユニットテストを追加 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR addresses suspected cache lifecycle issues (GPU textures / atlas nodes) by tightening pool limits, ensuring old cached GPU resources are released before overwrites, and refining CacheStore “trash/timer” behavior to avoid unintended wipes and render races.
Changes:
- Add per-bucket and total caps to the WebGPU fill/render texture pools, destroying textures when limits are exceeded (with new unit tests).
- Fix GPU/atlas leaks by releasing previously cached filter textures/attachments before overwriting cache entries (WebGL/WebGPU), and by removing stale atlas Nodes on MISS paths when Main/Worker caches get out of sync.
- Improve cache cleanup orchestration: add
CacheStore.cancelRemoveTimer, cancel scheduled removals onget(), and centralize cache-value disposal viaContext.releaseTextureCache()used byCommandRemoveCacheService.
Reviewed changes
Copilot reviewed 27 out of 28 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/index.ts | Bumps displayed player version string. |
| packages/webgpu/src/FillTexturePool.ts | Adds bucket/total caps and count tracking for pooled GPUTextures. |
| packages/webgpu/src/FillTexturePool.test.ts | New tests validating pool reuse and destruction when limits are exceeded. |
| packages/webgpu/src/Context/usecase/ContextContainerEndLayerUseCase.ts | Releases old cached filter attachment before overwriting cache entry. |
| packages/webgpu/src/Context.ts | Adds releaseTextureCache() to dispose Node/attachment-like cache values. |
| packages/webgl/src/Context/usecase/ContextContainerEndLayerUseCase.ts | Releases old cached filter texture before overwriting cache entry. |
| packages/webgl/src/Context/usecase/ContextApplyFilterUseCase.ts | Releases cached texture when filter key changes; avoids reuse when cache is null. |
| packages/webgl/src/Context.ts | Adds releaseTextureCache() to dispose Node/texture-like cache values. |
| packages/text/src/TextField/usecase/TextFieldResetUseCase.ts | Stops pushing removeIds from Main to avoid Worker wipe race; adds rationale comment. |
| packages/renderer/src/Video/usecase/VideoRenderUseCase.ts | Removes stale cached Node before creating a new Node on MISS to prevent atlas leaks. |
| packages/renderer/src/Video/usecase/VideoRenderUseCase.test.ts | Updates RendererUtil mock to include removeNode. |
| packages/renderer/src/TextField/usecase/TextFieldRenderUseCase.ts | Removes stale cached Node before creating a new Node on MISS. |
| packages/renderer/src/Shape/usecase/ShapeRenderUseCase.ts | Removes stale cached Node before creating a new Node on MISS (bitmap and scaled paths). |
| packages/renderer/src/Command/service/CommandRemoveCacheService.ts | Uses Context.releaseTextureCache() to dispose mixed cache values safely. |
| packages/renderer/src/Command/service/CommandRemoveCacheService.test.ts | Updates expectations to match releaseTextureCache() usage. |
| packages/display/src/Shape/usecase/ShapeClearBitmapBufferUseCase.ts | Stops pushing removeIds from Main to avoid Worker wipe race; adds rationale comment. |
| packages/display/src/MovieClip/service/MovieClipGetChildrenService.ts | Schedules cleanup by instanceId (timer if main entry exists, else push removeIds). |
| packages/display/src/DisplayObjectContainer/usecase/DisplayObjectContainerGenerateRenderQueueUseCase.ts | Changes container filter cache marker from dynamic key to "filterKey" value; adjusts HIT/MISS flow. |
| packages/display/src/DisplayObject/service/DisplayObjectDispatchRemovedToStageEventService.ts | Adds instanceId-based cache cleanup scheduling on removal. |
| packages/display/src/DisplayObject/service/DisplayObjectDispatchAddedToStageEventService.ts | Cancels pending remove timers on re-add to prevent delayed wipe after revival. |
| packages/cache/src/CacheStore/service/CacheStoreGetService.ts | Cancels pending deletion on get() by removing from trash_store and clearing marker. |
| packages/cache/src/CacheStore/service/CacheStoreGetService.test.ts | Updates tests for new signature and adds trash-cancel coverage. |
| packages/cache/src/CacheStore/service/CacheStoreCancelRemoveTimerService.ts | New service to cancel scheduled removal and clear "trash" marker. |
| packages/cache/src/CacheStore/service/CacheStoreCancelRemoveTimerService.test.ts | New tests for cancelRemoveTimer behavior. |
| packages/cache/src/CacheStore.ts | Adds cancelRemoveTimer() API; routes get() through updated GetService signature. |
| packages/cache/src/CacheStore.test.ts | Adds integration-style tests for trash flow (but includes a misnamed test title). |
| package.json | Version bump to 3.3.0 and dev dependency patch bumps. |
| package-lock.json | Lockfile updates consistent with package.json changes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| data.delete("trash"); | ||
| } | ||
|
|
||
| return data.get(key) || null; |
|
|
||
| describe("CacheStore trash flow integration", () => | ||
| { | ||
| it("removeTimer -> get -> removeTimerScheduledCache should still delete the entry", () => |
Comment on lines
55
to
68
| const hasNode = Boolean(render_queue[index++]); | ||
|
|
||
| node = hasNode | ||
| ? $cacheStore.get(uniqueKey, `${cacheKey}`) as Node | ||
| : $context.createNode(width, height); | ||
|
|
||
| if (!hasNode) { | ||
| if (hasNode) { | ||
| node = $cacheStore.get(uniqueKey, `${cacheKey}`) as Node; | ||
| } else { | ||
| // Main 側のみ wipe された race を考慮し、新規作成前に旧 Node を解放 | ||
| // (Shape/TextField MISS パスと同じ防御策。通常は no-op) | ||
| const oldNode = $cacheStore.get(uniqueKey, `${cacheKey}`) as Node | null; | ||
| if (oldNode) { | ||
| $context.removeNode(oldNode); | ||
| } | ||
| node = $context.createNode(width, height); | ||
| $cacheStore.set(uniqueKey, `${cacheKey}`, node); | ||
| } |
Comment on lines
64
to
77
| const hasNode = Boolean(render_queue[index++]); | ||
|
|
||
| node = hasNode | ||
| ? $cacheStore.get(uniqueKey, `${cacheKey}`) as Node | ||
| : $context.createNode(width, height); | ||
|
|
||
| if (!hasNode) { | ||
| if (hasNode) { | ||
| node = $cacheStore.get(uniqueKey, `${cacheKey}`) as Node; | ||
| } else { | ||
| // TextFieldResetUseCase 等で Main 側のみ wipe された場合、 | ||
| // Worker には旧 Node が残っているため、新規作成前に解放してアトラスリーク防止 | ||
| const oldNode = $cacheStore.get(uniqueKey, `${cacheKey}`) as Node | null; | ||
| if (oldNode) { | ||
| $context.removeNode(oldNode); | ||
| } | ||
| node = $context.createNode(width, height); | ||
| $cacheStore.set(uniqueKey, `${cacheKey}`, node); | ||
| } |
Comment on lines
+74
to
82
| // ShapeClearBitmapBufferUseCase 等で Main 側のみ wipe された場合、 | ||
| // Worker には旧 Node が残っているため、新規作成前に解放してアトラスリーク防止 | ||
| const oldBitmapNode = $cacheStore.get(uniqueKey, `${cacheKey}`) as Node | null; | ||
| if (oldBitmapNode) { | ||
| $context.removeNode(oldBitmapNode); | ||
| } | ||
| node = $context.createNode(width, height); | ||
| $cacheStore.set(uniqueKey, `${cacheKey}`, node); | ||
|
|
Comment on lines
+117
to
125
| // ShapeClearBitmapBufferUseCase 等で Main 側のみ wipe された場合、 | ||
| // Worker には旧 Node が残っているため、新規作成前に解放してアトラスリーク防止 | ||
| const oldNode = $cacheStore.get(uniqueKey, `${cacheKey}`) as Node | null; | ||
| if (oldNode) { | ||
| $context.removeNode(oldNode); | ||
| } | ||
| node = $context.createNode(width, height); | ||
| $cacheStore.set(uniqueKey, `${cacheKey}`, node); | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.