Skip to content

Commit

Permalink
feat: option to use Arraybuffer for volume loader instead of sharedAr…
Browse files Browse the repository at this point in the history
…rayBuffer
  • Loading branch information
Ouwen committed Jan 15, 2023
1 parent 5303d1a commit 85098d8
Show file tree
Hide file tree
Showing 9 changed files with 2,531 additions and 2,375 deletions.
4,738 changes: 2,379 additions & 2,359 deletions common/reviews/api/core.api.md

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion common/reviews/api/streaming-image-volume-loader.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1146,6 +1146,15 @@ type ScalingParameters = {
suvbsa?: number;
};

// @public
enum SharedArrayBufferModes {
AUTO = 'auto',
// (undocumented)
FALSE = 'false',
// (undocumented)
TRUE = 'true',
}

// @public
type StackNewImageEvent = CustomEvent_2<StackNewImageEventDetail>;

Expand Down Expand Up @@ -1207,7 +1216,7 @@ export class StreamingImageVolume extends ImageVolume {
imageIdIndex: number;
options: {
targetBuffer: {
arrayBuffer: ArrayBufferLike;
arrayBuffer: SharedArrayBuffer;
offset: number;
length: number;
type: any;
Expand Down
11 changes: 11 additions & 0 deletions packages/core/src/enums/SharedArrayBufferModes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* SharedArrayBuffer Modes
*/
enum SharedArrayBufferModes {
TRUE = 'true',
FALSE = 'false',
/** use SharedArrayBuffer if avalaible */
AUTO = 'auto',
}

export default SharedArrayBufferModes;
2 changes: 2 additions & 0 deletions packages/core/src/enums/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ViewportType from './ViewportType';
import InterpolationType from './InterpolationType';
import BlendModes from './BlendModes';
import OrientationAxis from './OrientationAxis';
import SharedArrayBufferModes from './SharedArrayBufferModes';

export {
Events,
Expand All @@ -12,4 +13,5 @@ export {
RequestType,
ViewportType,
OrientationAxis,
SharedArrayBufferModes,
};
7 changes: 7 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ import * as metaData from './metaData';
import {
init,
getShouldUseCPURendering,
getShouldUseSharedArrayBuffer,
isCornerstoneInitialized,
setUseCPURendering,
setUseSharedArrayBuffer,
resetUseCPURendering,
resetUseSharedArrayBuffer,
} from './init';

// Classes
Expand Down Expand Up @@ -102,4 +105,8 @@ export {
getShouldUseCPURendering,
setUseCPURendering,
resetUseCPURendering,
// SharedArrayBuffer
getShouldUseSharedArrayBuffer,
setUseSharedArrayBuffer,
resetUseSharedArrayBuffer,
};
65 changes: 63 additions & 2 deletions packages/core/src/init.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { getGPUTier } from 'detect-gpu';

import { Enums } from '@cornerstonejs/core';
let csRenderInitialized = false;
let useCPURendering = false;
let useSharedArrayBuffer = true;
let sharedArrayBufferMode = Enums.SharedArrayBufferModes.TRUE;

// https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/By_example/Detect_WebGL
function hasActiveWebGLContext() {
Expand All @@ -21,6 +23,18 @@ function hasActiveWebGLContext() {
return false;
}

function hasSharedArrayBuffer() {
try {
if (new SharedArrayBuffer(0)) {
return true;
} else {
return false;
}
} catch {
return false;
}
}

/**
* Initialize the cornerstone-core. If the browser has a webgl context and
* the detected gpu (by detect-gpu library) indicates the GPU is not low end we
Expand Down Expand Up @@ -55,6 +69,9 @@ async function init(defaultConfiguration = {}): Promise<boolean> {
console.log('CornerstoneRender: using GPU rendering');
}
}

setUseSharedArrayBuffer(sharedArrayBufferMode);

csRenderInitialized = true;
return csRenderInitialized;
}
Expand All @@ -78,7 +95,7 @@ function setUseCPURendering(status: boolean): void {
* @category Initialization
*
*/
function resetUseCPURendering() {
function resetUseCPURendering(): void {
useCPURendering = !hasActiveWebGLContext();
}

Expand All @@ -92,6 +109,47 @@ function getShouldUseCPURendering(): boolean {
return useCPURendering;
}

function setUseSharedArrayBuffer(
mode: Enums.SharedArrayBufferModes | boolean
): void {
if (mode == Enums.SharedArrayBufferModes.AUTO) {
sharedArrayBufferMode = Enums.SharedArrayBufferModes.AUTO;
const hasSharedBuffer = hasSharedArrayBuffer();
if (!hasSharedBuffer) {
useSharedArrayBuffer = false;
console.warn(
`CornerstoneRender: SharedArray Buffer not allowed, performance may be slower.
Try ensuring page is cross-origin isolated to enable SharedArrayBuffer.`
);
} else {
useSharedArrayBuffer = true;
// eslint-disable-next-line no-console
console.log('CornerstoneRender: using SharedArrayBuffer');
}
return;
}

if (mode == Enums.SharedArrayBufferModes.TRUE || mode == true) {
sharedArrayBufferMode = Enums.SharedArrayBufferModes.TRUE;
useSharedArrayBuffer = true;
return;
}

if (mode == Enums.SharedArrayBufferModes.FALSE || mode == false) {
sharedArrayBufferMode = Enums.SharedArrayBufferModes.FALSE;
useSharedArrayBuffer = false;
return;
}
}

function resetUseSharedArrayBuffer(): void {
setUseSharedArrayBuffer(sharedArrayBufferMode);
}

function getShouldUseSharedArrayBuffer(): boolean {
return useSharedArrayBuffer;
}

/**
*
* Returns whether or not cornerstone-core has been initialized.
Expand All @@ -106,7 +164,10 @@ function isCornerstoneInitialized(): boolean {
export {
init,
getShouldUseCPURendering,
getShouldUseSharedArrayBuffer,
isCornerstoneInitialized,
setUseCPURendering,
setUseSharedArrayBuffer,
resetUseCPURendering,
resetUseSharedArrayBuffer,
};
41 changes: 39 additions & 2 deletions packages/streaming-image-volume-loader/src/StreamingImageVolume.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,33 @@ export default class StreamingImageVolume extends ImageVolume {
triggerEvent(eventTarget, Enums.Events.IMAGE_LOAD_ERROR, eventDetail);
}

function handleArrayBufferLoad(scalarData, image, options) {
if (scalarData.buffer instanceof ArrayBuffer) {
const offset = options.targetBuffer.offset; // in bytes
const length = options.targetBuffer.length; // in frames
try {
if (scalarData instanceof Float32Array) {
const bytesInFloat = 4;
const floatView = new Float32Array(image.pixelData);
if (floatView.length !== length) {
throw 'Error pixelData length does not match frame length';
}
scalarData.set(floatView, offset / bytesInFloat);
}
if (scalarData instanceof Uint8Array) {
const bytesInUint8 = 1;
const intView = new Uint8Array(image.pixelData);
if (intView.length !== length) {
throw 'Error pixelData length does not match frame length';
}
scalarData.set(intView, offset / bytesInUint8);
}
} catch (e) {
console.error(e);
}
}
}

const requests = imageIds.map((imageId, imageIdIndex) => {
if (cachedFrames[imageIdIndex]) {
framesLoaded++;
Expand Down Expand Up @@ -415,7 +442,13 @@ export default class StreamingImageVolume extends ImageVolume {
const options = {
// WADO Image Loader
targetBuffer: {
arrayBuffer,
// keeping this in the options means a large empty volume array buffer
// will be transferred to the worker. This is undesirable for streaming
// volume without shared array buffer because the target is now an empty
// 300-500MB volume array buffer. Instead the volume should be progressively
// set in the main thread.
arrayBuffer:
arrayBuffer instanceof ArrayBuffer ? undefined : arrayBuffer,
offset: imageIdIndex * lengthInBytes,
length,
type,
Expand All @@ -434,7 +467,11 @@ export default class StreamingImageVolume extends ImageVolume {
// when we load directly into the Volume cache
function callLoadImage(imageId, imageIdIndex, options) {
return imageLoader.loadImage(imageId, options).then(
() => {
(image) => {
// scalarData is the volume container we are progressively loading into
// image is the pixelData decoded from workers in cornerstoneWADOImageLoader
const scalarData = this.scalarData;
handleArrayBufferLoad(scalarData, image, options);
successCallback(imageIdIndex, imageId, scalingParameters);
},
(error) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Enums,
imageLoader,
imageLoadPoolManager,
getShouldUseSharedArrayBuffer,
} from '@cornerstonejs/core';
import type { Types } from '@cornerstonejs/core';
import { vec3 } from 'gl-matrix';
Expand Down Expand Up @@ -150,33 +151,41 @@ function cornerstoneStreamingImageVolumeLoader(
cache.decacheIfNecessaryUntilBytesAvailable(sizeInBytes);

let scalarData;

const useSharedArrayBuffer = getShouldUseSharedArrayBuffer();
switch (BitsAllocated) {
case 8:
if (signed) {
throw new Error(
'8 Bit signed images are not yet supported by this plugin.'
);
} else {
scalarData = createUint8SharedArray(
dimensions[0] * dimensions[1] * dimensions[2]
);
scalarData = useSharedArrayBuffer
? createUint8SharedArray(
dimensions[0] * dimensions[1] * dimensions[2]
)
: new Uint8Array(dimensions[0] * dimensions[1] * dimensions[2]);
}

break;

case 16:
scalarData = createFloat32SharedArray(
dimensions[0] * dimensions[1] * dimensions[2]
);
scalarData = useSharedArrayBuffer
? createFloat32SharedArray(
dimensions[0] * dimensions[1] * dimensions[2]
)
: new Float32Array(dimensions[0] * dimensions[1] * dimensions[2]);

break;

case 24:
// hacky because we don't support alpha channel in dicom
scalarData = createUint8SharedArray(
dimensions[0] * dimensions[1] * dimensions[2] * numComponents
);
scalarData = useSharedArrayBuffer
? createUint8SharedArray(
dimensions[0] * dimensions[1] * dimensions[2] * numComponents
)
: new Uint8Array(
dimensions[0] * dimensions[1] * dimensions[2] * numComponents
);

break;
}
Expand Down
2 changes: 1 addition & 1 deletion utils/ExampleRunner/example-runner-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ if (configuration.examples) {
// console.log('conf', conf);
shell.ShellString(conf).to(webpackConfigPath);
shell.cd(exBasePath);
shell.exec(`webpack serve --progress --config ${webpackConfigPath}`);
shell.exec(`webpack serve --host 0.0.0.0 --progress --config ${webpackConfigPath}`);
} else {
console.log('=> To run an example:');
console.log(' $ npm run example -- PUT_YOUR_EXAMPLE_NAME_HERE\n');
Expand Down

0 comments on commit 85098d8

Please sign in to comment.