From cae1224cab241fabbcb23fc2318e94371152a27b Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Wed, 16 Sep 2026 20:55:35 -0400 Subject: [PATCH 1/2] fix(rendering): release resources when views are torn down --- package-lock.json | 35 ++--- package.json | 2 +- src/components/tools/SelectTool.vue | 2 + src/components/vtk/VtkRenderWindowParent.vue | 11 +- .../__tests__/useVolumeThumbnailing.spec.ts | 139 ++++++++++++++++++ src/composables/useVolumeThumbnailing.ts | 32 +++- src/core/thumbnailers/volume-thumbnailer.ts | 30 ++++ src/core/vtk/__tests__/useVtkView.spec.ts | 108 ++++++++++++++ src/core/vtk/deleteInteractor.ts | 21 +++ src/core/vtk/releaseRenderWindow.ts | 40 +++++ src/core/vtk/useVtkView.ts | 41 +++--- 11 files changed, 407 insertions(+), 54 deletions(-) create mode 100644 src/composables/__tests__/useVolumeThumbnailing.spec.ts create mode 100644 src/core/vtk/__tests__/useVtkView.spec.ts create mode 100644 src/core/vtk/deleteInteractor.ts create mode 100644 src/core/vtk/releaseRenderWindow.ts diff --git a/package-lock.json b/package-lock.json index 242cf90ef..db7f994ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@itk-wasm/dicom": "^7.6.4", "@itk-wasm/image-io": "1.6.1", "@itk-wasm/morphological-contour-interpolation": "2.0.0", - "@kitware/vtk.js": "^36.2.0", + "@kitware/vtk.js": "^37.0.4", "@netlify/edge-functions": "^3.0.2", "@rollup/plugin-replace": "^6.0.3", "@sentry/vite-plugin": "^4.6.1", @@ -2636,9 +2636,9 @@ } }, "node_modules/@kitware/vtk.js": { - "version": "36.2.1", - "resolved": "https://registry.npmjs.org/@kitware/vtk.js/-/vtk.js-36.2.1.tgz", - "integrity": "sha512-u5V7jfYeve1WNXVl7bZbjp755t1/EMyiW2y0aQU7YZ7RteCfZ8mxk9jB3DTeo9pGcIsVr+E8SIQz2bXHHedPlg==", + "version": "37.0.4", + "resolved": "https://registry.npmjs.org/@kitware/vtk.js/-/vtk.js-37.0.4.tgz", + "integrity": "sha512-iBCeHRqiYXAQXkYXe8hoMijwgnweYaPVaZrNhyfL1TLR82bfmxiazfjirnixNksSJTpphU8q/7wX1G8SfNOiXA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -2646,9 +2646,8 @@ "commander": "9.2.0", "d3-scale": "4.0.2", "fast-deep-equal": "3.1.3", - "fflate": "0.7.3", + "fflate": "0.7.5", "gl-matrix": "3.4.3", - "globalthis": "1.0.3", "seedrandom": "3.0.5", "shelljs": "0.8.5", "spark-md5": "3.0.2", @@ -2662,7 +2661,7 @@ }, "peerDependencies": { "autoprefixer": "^10.4.7", - "wslink": ">=1.1.0 || ^2.0.0" + "wslink": ">=2.0.0" } }, "node_modules/@msgpack/msgpack": { @@ -11416,9 +11415,9 @@ } }, "node_modules/fflate": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.3.tgz", - "integrity": "sha512-0Zz1jOzJWERhyhsimS54VTqOteCNwRtIlh8isdL0AXLo0g7xNTfTL7oWrkmCnPhZGocKIkWHBistBrrpoNH3aw==", + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.5.tgz", + "integrity": "sha512-QieYf//cis6ywHNi5qW1+PXPQ4bC+XVJAtS4AXIML8P76GroEiOxm/oQtn1f02UkJY1+KsXMJcC+R2v/Eg4G3g==", "dev": true, "license": "MIT" }, @@ -12121,22 +12120,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", diff --git a/package.json b/package.json index b5d08518e..df82796ee 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@itk-wasm/dicom": "^7.6.4", "@itk-wasm/image-io": "1.6.1", "@itk-wasm/morphological-contour-interpolation": "2.0.0", - "@kitware/vtk.js": "^36.2.0", + "@kitware/vtk.js": "^37.0.4", "@netlify/edge-functions": "^3.0.2", "@rollup/plugin-replace": "^6.0.3", "@sentry/vite-plugin": "^4.6.1", diff --git a/src/components/tools/SelectTool.vue b/src/components/tools/SelectTool.vue index af6ad6c6f..7b6b99c4b 100644 --- a/src/components/tools/SelectTool.vue +++ b/src/components/tools/SelectTool.vue @@ -48,6 +48,8 @@ onVTKEvent( // whatever its last tracked mouse move resolved, which can be a different // position or, mid capture, nothing at all. const selectedData = await view.widgetManager.getSelectedDataForXY(x, y); + // the pick spans a capture, which the view teardown can outrun + if (view.widgetManager.isDeleted()) return; if ('widget' in selectedData) { const widget = selectedData.widget as Partial | null; diff --git a/src/components/vtk/VtkRenderWindowParent.vue b/src/components/vtk/VtkRenderWindowParent.vue index c4629c69d..ea64ef36d 100644 --- a/src/components/vtk/VtkRenderWindowParent.vue +++ b/src/components/vtk/VtkRenderWindowParent.vue @@ -1,8 +1,9 @@ diff --git a/src/components/vtk/VtkSegmentationSliceRepresentation.vue b/src/components/vtk/VtkSegmentationSliceRepresentation.vue index 033920fe4..cfc2a3599 100644 --- a/src/components/vtk/VtkSegmentationSliceRepresentation.vue +++ b/src/components/vtk/VtkSegmentationSliceRepresentation.vue @@ -1,5 +1,12 @@