From 63fa428b98a8879e9da7b17fa8f1c3e38b1958f2 Mon Sep 17 00:00:00 2001 From: Julien Finet Date: Mon, 13 Jul 2026 10:45:30 +0200 Subject: [PATCH 1/2] perf(camera): use getViewMatrix output parameter to avoid copy By providing the expected output array, one can avoid an array copy --- .../index.js | 3 +-- Sources/Rendering/Core/Camera/index.js | 21 ++++++++++--------- Sources/Rendering/OpenGL/Camera/index.js | 2 +- .../Widgets/Widgets3D/LineWidget/behavior.js | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Sources/Interaction/Manipulators/MouseCameraTrackballPanManipulatorAutoCenter/index.js b/Sources/Interaction/Manipulators/MouseCameraTrackballPanManipulatorAutoCenter/index.js index 76b7292ed85..8d91df2b9c0 100644 --- a/Sources/Interaction/Manipulators/MouseCameraTrackballPanManipulatorAutoCenter/index.js +++ b/Sources/Interaction/Manipulators/MouseCameraTrackballPanManipulatorAutoCenter/index.js @@ -71,8 +71,7 @@ function computeNewCenterOfRotation( function getCameraMatrix(renderer, tempMatrix) { const cam = renderer.getActiveCamera(); if (cam) { - mat4.copy(tempMatrix, cam.getViewMatrix()); - return tempMatrix; + return cam.getViewMatrix(tempMatrix); } return null; } diff --git a/Sources/Rendering/Core/Camera/index.js b/Sources/Rendering/Core/Camera/index.js index 15a00fdaabc..4ce991c9af4 100644 --- a/Sources/Rendering/Core/Camera/index.js +++ b/Sources/Rendering/Core/Camera/index.js @@ -49,10 +49,10 @@ function vtkCamera(publicAPI, model) { } publicAPI.orthogonalizeViewUp = () => { - const vt = publicAPI.getViewMatrix(); - model.viewUp[0] = vt[4]; - model.viewUp[1] = vt[5]; - model.viewUp[2] = vt[6]; + publicAPI.getViewMatrix(tmpMatrix); + model.viewUp[0] = tmpMatrix[4]; + model.viewUp[1] = tmpMatrix[5]; + model.viewUp[2] = tmpMatrix[6]; publicAPI.modified(); }; @@ -231,8 +231,8 @@ function vtkCamera(publicAPI, model) { const fp = model.focalPoint; // get the eye / camera position from the viewMatrix - const vt = publicAPI.getViewMatrix(); - const axis = [-vt[0], -vt[1], -vt[2]]; + publicAPI.getViewMatrix(tmpMatrix); + const axis = [-tmpMatrix[0], -tmpMatrix[1], -tmpMatrix[2]]; mat4.identity(trans); @@ -251,8 +251,8 @@ function vtkCamera(publicAPI, model) { publicAPI.pitch = (angle) => { const position = model.position; - const vt = publicAPI.getViewMatrix(); - const axis = [vt[0], vt[1], vt[2]]; + publicAPI.getViewMatrix(tmpMatrix); + const axis = [tmpMatrix[0], tmpMatrix[1], tmpMatrix[2]]; mat4.identity(trans); @@ -388,7 +388,7 @@ function vtkCamera(publicAPI, model) { publicAPI.computeCameraLightTransform = () => { // not sure if this is the correct transformation, based on the same funciton in VTK - mat4.copy(tmpMatrix, publicAPI.getViewMatrix()); + publicAPI.getViewMatrix(tmpMatrix); mat4.invert(tmpMatrix, tmpMatrix); mat4.fromScaling(tmpMatrix2, [ @@ -507,6 +507,7 @@ function vtkCamera(publicAPI, model) { }; publicAPI.getViewMatrix = (out = undefined) => { + // Note that out can be tmpMatrix const result = out ?? new Float64Array(16); if (model.viewMatrix) { @@ -529,7 +530,7 @@ function vtkCamera(publicAPI, model) { if (model.modelTransformMatrix) { mat4.multiply(result, model.modelTransformMatrix, tmpMatrix); - } else { + } else if (result != tmpMatrix) { mat4.copy(result, tmpMatrix); } return result; diff --git a/Sources/Rendering/OpenGL/Camera/index.js b/Sources/Rendering/OpenGL/Camera/index.js index fe6839ac4c8..37fd27b7d02 100644 --- a/Sources/Rendering/OpenGL/Camera/index.js +++ b/Sources/Rendering/OpenGL/Camera/index.js @@ -54,7 +54,7 @@ function vtkOpenGLCamera(publicAPI, model) { ren.getMTime() > model.keyMatrixTime.getMTime() || model.renderable.getMTime() > model.keyMatrixTime.getMTime() ) { - mat4.copy(model.keyMatrices.wcvc, model.renderable.getViewMatrix()); + model.renderable.getViewMatrix(model.keyMatrices.wcvc); mat3.fromMat4(model.keyMatrices.normalMatrix, model.keyMatrices.wcvc); mat3.invert( diff --git a/Sources/Widgets/Widgets3D/LineWidget/behavior.js b/Sources/Widgets/Widgets3D/LineWidget/behavior.js index 9c0813f938b..8d253913c66 100644 --- a/Sources/Widgets/Widgets3D/LineWidget/behavior.js +++ b/Sources/Widgets/Widgets3D/LineWidget/behavior.js @@ -131,10 +131,10 @@ export default function widgetBehavior(publicAPI, model) { publicAPI.rotateHandlesToFaceCamera = () => { model.representations[0].setViewMatrix( - Array.from(model._camera.getViewMatrix()) + model._camera.getViewMatrix() ); model.representations[1].setViewMatrix( - Array.from(model._camera.getViewMatrix()) + model._camera.getViewMatrix() ); }; From a70c01dcf0cc71993459101aa5fd1f38d07c721b Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Mon, 13 Jul 2026 10:04:11 -0400 Subject: [PATCH 2/2] perf(camera): build view matrix directly into out buffer Avoids getViewMatrix using the module-private tmpMatrix as scratch and also lets callers pass that same tmpMatrix in as the output buffer, which required a `result != tmpMatrix` guard and a "out can be tmpMatrix" caveat. --- Sources/Rendering/Core/Camera/index.js | 21 +++++++------------ .../Widgets/Widgets3D/LineWidget/behavior.js | 8 ++----- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/Sources/Rendering/Core/Camera/index.js b/Sources/Rendering/Core/Camera/index.js index 4ce991c9af4..e79ab427468 100644 --- a/Sources/Rendering/Core/Camera/index.js +++ b/Sources/Rendering/Core/Camera/index.js @@ -506,34 +506,29 @@ function vtkCamera(publicAPI, model) { } }; - publicAPI.getViewMatrix = (out = undefined) => { - // Note that out can be tmpMatrix - const result = out ?? new Float64Array(16); - + publicAPI.getViewMatrix = (out = new Float64Array(16)) => { if (model.viewMatrix) { if (model.modelTransformMatrix) { - mat4.multiply(result, model.modelTransformMatrix, model.viewMatrix); + mat4.multiply(out, model.modelTransformMatrix, model.viewMatrix); } else { - mat4.copy(result, model.viewMatrix); + mat4.copy(out, model.viewMatrix); } - return result; + return out; } mat4.lookAt( - tmpMatrix, + out, model.position, // eye model.focalPoint, // at model.viewUp // up ); - mat4.transpose(tmpMatrix, tmpMatrix); + mat4.transpose(out, out); if (model.modelTransformMatrix) { - mat4.multiply(result, model.modelTransformMatrix, tmpMatrix); - } else if (result != tmpMatrix) { - mat4.copy(result, tmpMatrix); + mat4.multiply(out, model.modelTransformMatrix, out); } - return result; + return out; }; publicAPI.setProjectionMatrix = (mat) => { diff --git a/Sources/Widgets/Widgets3D/LineWidget/behavior.js b/Sources/Widgets/Widgets3D/LineWidget/behavior.js index 8d253913c66..de2cec64366 100644 --- a/Sources/Widgets/Widgets3D/LineWidget/behavior.js +++ b/Sources/Widgets/Widgets3D/LineWidget/behavior.js @@ -130,12 +130,8 @@ export default function widgetBehavior(publicAPI, model) { }; publicAPI.rotateHandlesToFaceCamera = () => { - model.representations[0].setViewMatrix( - model._camera.getViewMatrix() - ); - model.representations[1].setViewMatrix( - model._camera.getViewMatrix() - ); + model.representations[0].setViewMatrix(model._camera.getViewMatrix()); + model.representations[1].setViewMatrix(model._camera.getViewMatrix()); }; // Handles visibility ---------------------------------------------------------