@@ -11,7 +11,7 @@ import vtkRenderPass from 'vtk.js/Sources/Rendering/SceneGraph/RenderPass';
1111import vtkRenderWindowViewNode from 'vtk.js/Sources/Rendering/SceneGraph/RenderWindowViewNode' ;
1212import HalfFloat from 'vtk.js/Sources/Common/Core/HalfFloat' ;
1313
14- const { vtkErrorMacro } = macro ;
14+ const { vtkErrorMacro, vtkWarningMacro } = macro ;
1515// const IS_CHROME = navigator.userAgent.indexOf('Chrome') !== -1;
1616const SCREENSHOT_PLACEHOLDER = {
1717 position : 'absolute' ,
@@ -31,6 +31,66 @@ function vtkWebGPURenderWindow(publicAPI, model) {
3131
3232 publicAPI . getViewNodeFactory = ( ) => model . myFactory ;
3333
34+ function releaseViewNodeResources ( viewNode , visited = new Set ( ) ) {
35+ if ( ! viewNode || visited . has ( viewNode ) ) {
36+ return ;
37+ }
38+ visited . add ( viewNode ) ;
39+
40+ const children = viewNode . getChildren ?. ( ) || [ ] ;
41+ for ( let i = 0 ; i < children . length ; i ++ ) {
42+ const child = children [ i ] ;
43+ child ?. releaseGraphicsResources ?. ( ) ;
44+ releaseViewNodeResources ( child , visited ) ;
45+ }
46+ }
47+
48+ function queueRenderAfterInitialization ( ) {
49+ const subscription = publicAPI . onInitialized ( ( ) => {
50+ subscription . unsubscribe ( ) ;
51+ if ( ! model . deleted ) {
52+ publicAPI . traverseAllPasses ( ) ;
53+ }
54+ } ) ;
55+ }
56+
57+ function handleDeviceLost ( info , deviceGeneration ) {
58+ if (
59+ model . deleted ||
60+ deviceGeneration !== model . deviceGeneration ||
61+ model . handlingDeviceLost
62+ ) {
63+ return ;
64+ }
65+
66+ model . handlingDeviceLost = true ;
67+ model . deviceLostInfo = info ;
68+
69+ const reason = info ?. reason ?? 'unknown' ;
70+ const message = info ?. message || 'WebGPU device was lost.' ;
71+ vtkWarningMacro ( `WebGPU device lost (${ reason } ): ${ message } ` ) ;
72+
73+ publicAPI . releaseGraphicsResources ( ) ;
74+ publicAPI . invokeDeviceLost ( {
75+ reason,
76+ message,
77+ recoverable : reason !== 'destroyed' ,
78+ } ) ;
79+
80+ if ( reason !== 'destroyed' ) {
81+ queueRenderAfterInitialization ( ) ;
82+ publicAPI . initialize ( ) ;
83+ }
84+
85+ model . handlingDeviceLost = false ;
86+ }
87+
88+ function watchForDeviceLoss ( deviceHandle , deviceGeneration ) {
89+ deviceHandle . lost . then ( ( info ) => {
90+ handleDeviceLost ( info , deviceGeneration ) ;
91+ } ) ;
92+ }
93+
3494 // Auto update style
3595 const previousSize = [ 0 , 0 ] ;
3696 function updateWindow ( ) {
@@ -128,6 +188,7 @@ function vtkWebGPURenderWindow(publicAPI, model) {
128188 publicAPI . initialize = ( ) => {
129189 if ( ! model . initializing ) {
130190 model . initializing = true ;
191+ model . deviceLostInfo = null ;
131192 if ( ! navigator . gpu ) {
132193 vtkErrorMacro ( 'WebGPU is not enabled.' ) ;
133194 return ;
@@ -213,20 +274,29 @@ function vtkWebGPURenderWindow(publicAPI, model) {
213274 model . device = null ;
214275 return ;
215276 }
216- // model.device.getHandle().lost.then((info) => {
217- // console.log(`${info.message}`);
218- // publicAPI.releaseGraphicsResources();
219- // });
277+ model . deviceGeneration += 1 ;
278+ watchForDeviceLoss ( model . device . getHandle ( ) , model . deviceGeneration ) ;
220279 model . context = model . canvas . getContext ( 'webgpu' ) ;
221280 } ;
222281
223282 publicAPI . releaseGraphicsResources = ( ) => {
283+ if ( model . renderPasses ) {
284+ for ( let i = 0 ; i < model . renderPasses . length ; i ++ ) {
285+ model . renderPasses [ i ] ?. releaseGraphicsResources ?. ( ) ;
286+ }
287+ }
288+ releaseViewNodeResources ( publicAPI ) ;
224289 const rp = vtkRenderPass . newInstance ( ) ;
225290 rp . setCurrentOperation ( 'Release' ) ;
226291 rp . traverse ( publicAPI , null ) ;
292+ if ( model . context ) {
293+ model . context . unconfigure ( ) ;
294+ }
227295 model . adapter = null ;
228296 model . device = null ;
229297 model . context = null ;
298+ model . commandEncoder = null ;
299+ model . _configured = false ;
230300 model . initialized = false ;
231301 model . initializing = false ;
232302 } ;
@@ -573,6 +643,10 @@ function vtkWebGPURenderWindow(publicAPI, model) {
573643
574644const DEFAULT_VALUES = {
575645 initialized : false ,
646+ initializing : false ,
647+ handlingDeviceLost : false ,
648+ deviceGeneration : 0 ,
649+ deviceLostInfo : null ,
576650 context : null ,
577651 adapter : null ,
578652 device : null ,
@@ -623,10 +697,12 @@ export function extend(publicAPI, model, initialValues = {}) {
623697
624698 macro . event ( publicAPI , model , 'imageReady' ) ;
625699 macro . event ( publicAPI , model , 'initialized' ) ;
700+ macro . event ( publicAPI , model , 'deviceLost' ) ;
626701
627702 // Build VTK API
628703 macro . get ( publicAPI , model , [
629704 'commandEncoder' ,
705+ 'deviceLostInfo' ,
630706 'device' ,
631707 'presentationFormat' ,
632708 'useBackgroundImage' ,
0 commit comments