Skip to content

Commit dd24960

Browse files
bruyeretfinetjul
authored andcommitted
feat(MultiRW): Add example on how to release graphics resources
Fix the sharing of graphics resources, when a texture is released
1 parent cc14000 commit dd24960

File tree

4 files changed

+97
-68
lines changed

4 files changed

+97
-68
lines changed

Examples/Rendering/ManyRenderWindows/index.js

Lines changed: 69 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -99,27 +99,26 @@ function createImageActor(imageData) {
9999
return actor;
100100
}
101101

102-
async function createActors(numberOfActors) {
102+
async function readImageData() {
103103
const reader = vtkHttpDataSetReader.newInstance({ fetchGzip: true });
104104
await reader.setUrl(`${__BASE_PATH__}/data/volume/LIDC2.vti`);
105105
await reader.loadData();
106106
const imageData = reader.getOutputData();
107107

108-
const actors = [];
109-
for (let i = 0; i < numberOfActors; ++i) {
110-
if (Math.random() > 0.5) {
111-
actors.push(createVolumeActor(imageData));
112-
} else {
113-
actors.push(createImageActor(imageData));
114-
}
115-
}
116-
117-
return actors;
108+
return imageData;
118109
}
119110

120-
const mainRenderWindow = vtkRenderWindow.newInstance();
121-
const mainRenderWindowView = mainRenderWindow.newAPISpecificView();
122-
mainRenderWindow.addView(mainRenderWindowView);
111+
let mainRenderWindow;
112+
let mainRenderWindowView;
113+
function resetMainRenderWindowAndView() {
114+
mainRenderWindow = vtkRenderWindow.newInstance();
115+
mainRenderWindowView = mainRenderWindow.newAPISpecificView();
116+
mainRenderWindow.addView(mainRenderWindowView);
117+
// Main view has to be initialized before the first "render" from a child render window
118+
// We initialize before creating the child render windows because the interactor initialization calls "render" on them
119+
mainRenderWindowView.initialize();
120+
}
121+
resetMainRenderWindowAndView();
123122

124123
const rootContainer = document.createElement('div');
125124
rootContainer.style.display = 'flex';
@@ -128,6 +127,10 @@ rootContainer.style['justify-content'] = 'space-between';
128127
rootContainer.style['flex-wrap'] = 'wrap';
129128
document.body.appendChild(rootContainer);
130129

130+
const addRenderWindowButton = document.createElement('button');
131+
addRenderWindowButton.innerText = 'Create a new render window';
132+
rootContainer.appendChild(addRenderWindowButton);
133+
131134
function applyStyle(element) {
132135
const width = Math.floor(200 + Math.random() * 200);
133136
const height = Math.floor(200 + Math.random() * 200);
@@ -146,7 +149,11 @@ function createRemoveButton() {
146149
return buttonEl;
147150
}
148151

149-
function addRenderWindow() {
152+
const childRenderWindows = [];
153+
function addRenderWindow(actor) {
154+
if (mainRenderWindow.isDeleted()) {
155+
resetMainRenderWindowAndView();
156+
}
150157
// Create a child renderwindow
151158
const renderWindow = vtkRenderWindow.newInstance();
152159
mainRenderWindow.addRenderWindow(renderWindow);
@@ -189,47 +196,66 @@ function addRenderWindow() {
189196
mainRenderWindow.removeRenderWindow(renderWindow);
190197
mainRenderWindowView.removeNode(renderWindowView);
191198
interactor.delete();
192-
renderWindow.delete();
193199
renderWindowView.delete();
200+
renderWindow.delete();
201+
if (mainRenderWindow.getChildRenderWindowsByReference().length === 0) {
202+
// When there is no child render window anymore, delete the main render window
203+
// We also release the graphics resources, which is not very import as when the context is destroyed, the resources should be freed
204+
// The release of shared graphics resources is not automatic and can be done by hand when the resource is not needed anymore
205+
const imageData = actor.getMapper().getInputData();
206+
const scalars = imageData.getPointData().getScalars();
207+
mainRenderWindowView.releaseGraphicsResourcesForObject(scalars);
208+
mainRenderWindowView.delete();
209+
mainRenderWindow.delete();
210+
}
194211
});
195212
container.appendChild(button);
196213

214+
// Create the corresponding renderer
215+
const background = [
216+
0.5 * Math.random() + 0.25,
217+
0.5 * Math.random() + 0.25,
218+
0.5 * Math.random() + 0.25,
219+
];
220+
const renderer = vtkRenderer.newInstance({ background });
221+
renderWindow.addRenderer(renderer);
222+
223+
// Add the actor and reset camera
224+
renderer.addActor(actor);
225+
const camera = renderer.getActiveCamera();
226+
camera.yaw(90);
227+
camera.roll(90);
228+
camera.azimuth(Math.random() * 360);
229+
renderer.resetCamera();
230+
231+
childRenderWindows.push(renderWindow);
232+
197233
return renderWindow;
198234
}
199235

236+
function createRandomActor(imageData) {
237+
if (Math.random() > 0.5) {
238+
return createVolumeActor(imageData);
239+
}
240+
return createImageActor(imageData);
241+
}
242+
200243
// ----------------------------------------------------------------------------
201244
// Fill up page
202245
// ----------------------------------------------------------------------------
203246

204-
const childRenderWindows = [];
205-
createActors(64).then((actors) => {
206-
// Main view has to be initialized before the first "render" from a child render window
207-
// We initialize before creating the child render windows because the interactor initialization calls "render" on them
208-
mainRenderWindowView.initialize();
209-
210-
actors.forEach((actor) => {
211-
const childRenderWindow = addRenderWindow();
212-
213-
// Create the corresponding renderer
214-
const background = [
215-
0.5 * Math.random() + 0.25,
216-
0.5 * Math.random() + 0.25,
217-
0.5 * Math.random() + 0.25,
218-
];
219-
const renderer = vtkRenderer.newInstance({ background });
220-
childRenderWindow.addRenderer(renderer);
221-
222-
// Add the actor and reset camera
223-
renderer.addActor(actor);
224-
const camera = renderer.getActiveCamera();
225-
camera.yaw(90);
226-
camera.roll(90);
227-
camera.azimuth(Math.random() * 360);
228-
renderer.resetCamera();
229-
230-
childRenderWindows.push(childRenderWindow);
247+
readImageData().then((imageData) => {
248+
// The button to add render windows
249+
addRenderWindowButton.addEventListener('click', () => {
250+
addRenderWindow(createRandomActor(imageData));
251+
mainRenderWindowView.resizeFromChildRenderWindows();
252+
mainRenderWindow.render();
231253
});
232254

255+
// Create several render windows and do the first resize and render
256+
for (let i = 0; i < 64; i++) {
257+
addRenderWindow(createRandomActor(imageData));
258+
}
233259
mainRenderWindowView.resizeFromChildRenderWindows();
234260
mainRenderWindow.render();
235261
});

Sources/Rendering/OpenGL/ImageMapper/index.js

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -882,19 +882,16 @@ function vtkOpenGLImageMapper(publicAPI, model) {
882882
}
883883
};
884884

885-
publicAPI.getNeedToRebuildBufferObjects = (ren, actor) => {
886-
// first do a coarse check
887-
if (
888-
model.VBOBuildTime.getMTime() < publicAPI.getMTime() ||
889-
model.VBOBuildTime.getMTime() < actor.getMTime() ||
890-
model.VBOBuildTime.getMTime() < model.renderable.getMTime() ||
891-
model.VBOBuildTime.getMTime() < actor.getProperty().getMTime() ||
892-
model.VBOBuildTime.getMTime() < model.currentInput.getMTime()
893-
) {
894-
return true;
895-
}
896-
return false;
897-
};
885+
publicAPI.getNeedToRebuildBufferObjects = (ren, actor) =>
886+
model.VBOBuildTime.getMTime() < publicAPI.getMTime() ||
887+
model.VBOBuildTime.getMTime() < actor.getMTime() ||
888+
model.VBOBuildTime.getMTime() < model.renderable.getMTime() ||
889+
model.VBOBuildTime.getMTime() < actor.getProperty().getMTime() ||
890+
model.VBOBuildTime.getMTime() < model.currentInput.getMTime() ||
891+
!model.openGLTexture?.getHandle() ||
892+
!model.colorTexture?.getHandle() ||
893+
!model.labelOutlineThicknessTexture?.getHandle() ||
894+
!model.pwfTexture?.getHandle();
898895

899896
publicAPI.buildBufferObjects = (ren, actor) => {
900897
const image = model.currentInput;
@@ -930,7 +927,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
930927
model._openGLRenderWindow.getGraphicsResourceForObject(colorTransferFunc);
931928

932929
const reBuildC =
933-
!cTex?.vtkObj ||
930+
!cTex?.vtkObj?.getHandle() ||
934931
cTex?.hash !== cfunToString ||
935932
model.colorTextureString !== cfunToString;
936933
if (reBuildC) {
@@ -1016,7 +1013,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
10161013
model._openGLRenderWindow.getGraphicsResourceForObject(pwFunc);
10171014
// rebuild opacity tfun?
10181015
const reBuildPwf =
1019-
!pwfTex?.vtkObj ||
1016+
!pwfTex?.vtkObj?.getHandle() ||
10201017
pwfTex?.hash !== pwfunToString ||
10211018
model.pwfTextureString !== pwfunToString;
10221019
if (reBuildPwf) {
@@ -1275,7 +1272,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
12751272

12761273
const tex =
12771274
model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
1278-
if (!tex?.vtkObj) {
1275+
if (!tex?.vtkObj?.getHandle()) {
12791276
if (model._scalars !== scalars) {
12801277
model._openGLRenderWindow.releaseGraphicsResourcesForObject(
12811278
model._scalars
@@ -1363,7 +1360,7 @@ function vtkOpenGLImageMapper(publicAPI, model) {
13631360
const toString = `${labelOutlineThicknessArray.join('-')}`;
13641361

13651362
const reBuildL =
1366-
!lTex?.vtkObj ||
1363+
!lTex?.vtkObj?.getHandle() ||
13671364
lTex?.hash !== toString ||
13681365
model.labelOutlineThicknessTextureString !== toString;
13691366

Sources/Rendering/OpenGL/ImageResliceMapper/index.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,10 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
190190
model.VBOBuildTime.getMTime() < model.renderable.getMTime() ||
191191
model.VBOBuildTime.getMTime() < actor.getProperty().getMTime() ||
192192
model.VBOBuildTime.getMTime() < model.currentInput.getMTime() ||
193-
model.VBOBuildTime.getMTime() < model.resliceGeom.getMTime();
193+
model.VBOBuildTime.getMTime() < model.resliceGeom.getMTime() ||
194+
!model.openGLTexture?.getHandle() ||
195+
!model.colorTexture?.getHandle() ||
196+
!model.pwfTexture?.getHandle();
194197

195198
publicAPI.buildBufferObjects = (ren, actor) => {
196199
const image = model.currentInput;
@@ -214,7 +217,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
214217
let toString = `${image.getMTime()}A${scalars.getMTime()}`;
215218

216219
const tex = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
217-
const reBuildTex = !tex?.vtkObj || tex?.hash !== toString;
220+
const reBuildTex = !tex?.vtkObj?.getHandle() || tex?.hash !== toString;
218221
if (reBuildTex) {
219222
if (!model.openGLTexture) {
220223
model.openGLTexture = vtkOpenGLTexture.newInstance();
@@ -255,7 +258,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
255258
const cTex =
256259
model._openGLRenderWindow.getGraphicsResourceForObject(colorTransferFunc);
257260
const reBuildC =
258-
!cTex?.vtkObj ||
261+
!cTex?.vtkObj?.getHandle() ||
259262
cTex?.hash !== toString ||
260263
model.colorTextureString !== toString;
261264
if (reBuildC) {
@@ -332,7 +335,7 @@ function vtkOpenGLImageResliceMapper(publicAPI, model) {
332335
model._openGLRenderWindow.getGraphicsResourceForObject(pwFunc);
333336
// rebuild opacity tfun?
334337
const reBuildPwf =
335-
!pwfTex?.vtkObj ||
338+
!pwfTex?.vtkObj?.getHandle() ||
336339
pwfTex?.hash !== toString ||
337340
model.pwfTextureString !== toString;
338341
if (reBuildPwf) {

Sources/Rendering/OpenGL/VolumeMapper/index.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,7 +1476,10 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
14761476
model.VBOBuildTime.getMTime() < actor.getMTime() ||
14771477
model.VBOBuildTime.getMTime() < model.renderable.getMTime() ||
14781478
model.VBOBuildTime.getMTime() < actor.getProperty().getMTime() ||
1479-
model.VBOBuildTime.getMTime() < model.currentInput.getMTime()
1479+
model.VBOBuildTime.getMTime() < model.currentInput.getMTime() ||
1480+
!model.scalarTexture?.getHandle() ||
1481+
!model.colorTexture?.getHandle() ||
1482+
!model.labelOutlineThicknessTexture?.getHandle()
14801483
) {
14811484
return true;
14821485
}
@@ -1614,7 +1617,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
16141617
const cTex =
16151618
model._openGLRenderWindow.getGraphicsResourceForObject(colorTransferFunc);
16161619
const reBuildC =
1617-
!cTex?.vtkObj ||
1620+
!cTex?.vtkObj?.getHandle() ||
16181621
cTex?.hash !== toString ||
16191622
model.colorTextureString !== toString;
16201623
if (reBuildC) {
@@ -1663,7 +1666,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
16631666
const tex = model._openGLRenderWindow.getGraphicsResourceForObject(scalars);
16641667
// rebuild the scalarTexture if the data has changed
16651668
toString = `${image.getMTime()}A${scalars.getMTime()}`;
1666-
const reBuildTex = !tex?.vtkObj || tex?.hash !== toString;
1669+
const reBuildTex = !tex?.vtkObj?.getHandle() || tex?.hash !== toString;
16671670
if (reBuildTex) {
16681671
// Build the textures
16691672
const dims = image.getDimensions();
@@ -1769,7 +1772,7 @@ function vtkOpenGLVolumeMapper(publicAPI, model) {
17691772
const toString = `${labelOutlineThicknessArray.join('-')}`;
17701773

17711774
const reBuildL =
1772-
!lTex?.vtkObj ||
1775+
!lTex?.vtkObj?.getHandle() ||
17731776
lTex?.hash !== toString ||
17741777
model.labelOutlineThicknessTextureString !== toString;
17751778

0 commit comments

Comments
 (0)