From 47d4061978af5bcff00c53d66d2ffdb5de524c5d Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Sun, 11 Feb 2018 23:43:09 -0500 Subject: [PATCH 1/3] perf(Math): Create and use arrayMin and arrayMax Create and use vtkMath.arrayMin and vtkMath.arrayMax as suggested by: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Using_apply_and_built-in_functions Apply in Texture and PiecewiseGaussianWidget. This improves volume rendering performance by ~15%. --- Sources/Common/Core/Math/api.md | 8 ++--- Sources/Common/Core/Math/index.js | 30 +++++++++++++++++++ .../Widgets/PiecewiseGaussianWidget/index.js | 10 ++----- Sources/Rendering/OpenGL/Texture/index.js | 17 +++++------ 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/Sources/Common/Core/Math/api.md b/Sources/Common/Core/Math/api.md index 91bdf1eb023..b6631251860 100644 --- a/Sources/Common/Core/Math/api.md +++ b/Sources/Common/Core/Math/api.md @@ -34,13 +34,13 @@ Same as Math.min(). Same as Math.max(). -### isPowerOfTwo(number) : Boolean +### arrayMin(array) -NOT IMPLEMENTED +Minimum of the array. -### nearestPowerOfTwo(number) : int +### arrayMax(a, b) -NOT IMPLEMENTED +Maximum of the array. ### factorial(n) : number diff --git a/Sources/Common/Core/Math/index.js b/Sources/Common/Core/Math/index.js index 3360b75a937..3a3cd9cc420 100644 --- a/Sources/Common/Core/Math/index.js +++ b/Sources/Common/Core/Math/index.js @@ -12,6 +12,7 @@ const { vtkErrorMacro, vtkWarningMacro } = macro; let randomSeedValue = 0; const VTK_MAX_ROTATIONS = 20; const VTK_SMALL_NUMBER = 1.0e-12; +const MAX_FUNCTION_ARGUMENTS = 32768; function notImplemented(method) { return () => vtkErrorMacro(`vtkMath::${method} - NOT IMPLEMENTED`); @@ -41,6 +42,33 @@ const Pi = () => Math.PI; const radiansFromDegrees = (deg) => deg / 180 * Math.PI; const degreesFromRadians = (rad) => rad * 180 / Math.PI; const { round, floor, ceil, min, max } = Math; + +function arrayMin(arr) { + let minValue = Infinity; + for (let i = 0, len = arr.length; i < len; i += MAX_FUNCTION_ARGUMENTS) { + const submin = Math.min.apply( + null, + arr.slice(i, Math.min(i + MAX_FUNCTION_ARGUMENTS, len)) + ); + minValue = Math.min(submin, minValue); + } + + return minValue; +} + +function arrayMax(arr) { + let maxValue = -Infinity; + for (let i = 0, len = arr.length; i < len; i += MAX_FUNCTION_ARGUMENTS) { + const submax = Math.max.apply( + null, + arr.slice(i, Math.min(i + MAX_FUNCTION_ARGUMENTS, len)) + ); + maxValue = Math.max(submax, maxValue); + } + + return maxValue; +} + const ceilLog2 = notImplemented('ceilLog2'); const factorial = notImplemented('factorial'); @@ -1993,6 +2021,8 @@ export default { ceilLog2, min, max, + arrayMin, + arrayMax, isPowerOfTwo, nearestPowerOfTwo, factorial, diff --git a/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js b/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js index e323aaf0148..69326e121a1 100644 --- a/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js +++ b/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js @@ -1,4 +1,5 @@ import macro from 'vtk.js/Sources/macro'; +import vtkMath from 'vtk.js/Sources/Common/Core/Math'; /* eslint-disable no-continue */ @@ -438,13 +439,8 @@ function vtkPiecewiseGaussianWidget(publicAPI, model) { numberOfBinsToSkip = 1 ) => { model.histogramArray = array; - const size = array.length; - let max = array[0]; - let min = array[0]; - for (let i = 1; i < size; i++) { - max = Math.max(max, array[i]); - min = Math.min(min, array[i]); - } + const max = vtkMath.arrayMax(array); + const min = vtkMath.arrayMin(array); const delta = max - min; model.dataRange = [min, max]; diff --git a/Sources/Rendering/OpenGL/Texture/index.js b/Sources/Rendering/OpenGL/Texture/index.js index ca1b945029e..b23c4c3cb8c 100644 --- a/Sources/Rendering/OpenGL/Texture/index.js +++ b/Sources/Rendering/OpenGL/Texture/index.js @@ -930,12 +930,8 @@ function vtkOpenGLTexture(publicAPI, model) { const numPixelsIn = width * height * depth; // compute min and max values - let min = data[0]; - let max = data[0]; - for (let i = 0; i < numPixelsIn; ++i) { - min = Math.min(min, data[i]); - max = Math.max(max, data[i]); - } + const min = vtkMath.arrayMin(data); + let max = vtkMath.arrayMax(data); if (min === max) { max = min + 1.0; } @@ -1115,6 +1111,7 @@ function vtkOpenGLTexture(publicAPI, model) { // have to compute the gradient to get the normal // and magnitude const tmpArray = new Float32Array(width * height * depth * 4); + const tmpMagArray = new Float32Array(width * height * depth); let inPtr = 0; let outPtr = 0; @@ -1151,18 +1148,20 @@ function vtkOpenGLTexture(publicAPI, model) { ); const mag = vec3.length(grad); - minMag = Math.min(mag, minMag); - maxMag = Math.max(mag, maxMag); - vec3.normalize(grad, grad); tmpArray[outPtr++] = grad[0]; tmpArray[outPtr++] = grad[1]; tmpArray[outPtr++] = grad[2]; tmpArray[outPtr++] = mag; + tmpMagArray[inPtr] = mag; inPtr++; } } } + const arrayMinMag = vtkMath.arrayMin(tmpMagArray); + const arrayMaxMag = vtkMath.arrayMax(tmpMagArray); + minMag = Math.min(arrayMinMag, minMag); + maxMag = Math.max(arrayMaxMag, maxMag); // store the information, we will need it later model.volumeInfo = { min: minMag, max: maxMag }; From 5e6a553d5fb69cff5c989f015d2af6dd6f13fdd6 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Sun, 11 Feb 2018 23:59:08 -0500 Subject: [PATCH 2/3] perf(PiecewiseGaussianWidget): Replace forEach with for loop in setDataArray Results in ~5% speedup in volume rendering. --- .../Interaction/Widgets/PiecewiseGaussianWidget/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js b/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js index 69326e121a1..411c5643cba 100644 --- a/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js +++ b/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js @@ -448,12 +448,12 @@ function vtkPiecewiseGaussianWidget(publicAPI, model) { while (model.histogram.length < model.numberOfBins) { model.histogram.push(0); } - array.forEach((value) => { + for(let i = 0, len = array.length; i < len; i++) { const idx = Math.floor( - (model.numberOfBins - 1) * (Number(value) - min) / delta + (model.numberOfBins - 1) * (Number(array[i]) - min) / delta ); model.histogram[idx] += 1; - }); + } // Smart Rescale Histogram const sampleSize = Math.min( From 650be91f1836bc2454e29c34750d9dd611273198 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Mon, 12 Feb 2018 07:32:02 -0500 Subject: [PATCH 3/3] style(Texture): Reformat for prettier --- Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js | 2 +- Sources/Rendering/OpenGL/Texture/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js b/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js index 411c5643cba..0f0553eba88 100644 --- a/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js +++ b/Sources/Interaction/Widgets/PiecewiseGaussianWidget/index.js @@ -448,7 +448,7 @@ function vtkPiecewiseGaussianWidget(publicAPI, model) { while (model.histogram.length < model.numberOfBins) { model.histogram.push(0); } - for(let i = 0, len = array.length; i < len; i++) { + for (let i = 0, len = array.length; i < len; i++) { const idx = Math.floor( (model.numberOfBins - 1) * (Number(array[i]) - min) / delta ); diff --git a/Sources/Rendering/OpenGL/Texture/index.js b/Sources/Rendering/OpenGL/Texture/index.js index b23c4c3cb8c..cf85dc03d57 100644 --- a/Sources/Rendering/OpenGL/Texture/index.js +++ b/Sources/Rendering/OpenGL/Texture/index.js @@ -1153,7 +1153,7 @@ function vtkOpenGLTexture(publicAPI, model) { tmpArray[outPtr++] = grad[1]; tmpArray[outPtr++] = grad[2]; tmpArray[outPtr++] = mag; - tmpMagArray[inPtr] = mag; + tmpMagArray[inPtr] = mag; inPtr++; } }