Skip to content

Commit f80e8cc

Browse files
committed
Workaround for WebGL crash for datasets with many segmentation layers (#6995)
* reduce shader complexity when having multiple segmentation layers (shader needs to be compiled more often then, though) * fix tests * clean up * update changelog
1 parent d7a072e commit f80e8cc

File tree

3 files changed

+57
-55
lines changed

3 files changed

+57
-55
lines changed

CHANGELOG.unreleased.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
1616

1717
### Fixed
1818
- Fixed rendering issues on some affected systems that led to "black holes". [#7018](https://github.com/scalableminds/webknossos/pull/7018)
19+
- Added a workaround for a WebGL crash which could appear when a dataset contained many segmentation layers. [#6995](https://github.com/scalableminds/webknossos/pull/6995)
1920

2021
### Removed
2122

frontend/javascripts/oxalis/model/bucket_data_handling/data_rendering_logic.tsx

+11-1
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ export function calculateTextureSizeAndCountForLayer(
200200
function buildTextureInformationMap<
201201
Layer extends {
202202
elementClass: ElementClass;
203+
category: "color" | "segmentation";
203204
},
204205
>(
205206
layers: Array<Layer>,
@@ -238,6 +239,7 @@ function getSmallestCommonBucketCapacity<
238239
function getRenderSupportedLayerCount<
239240
Layer extends {
240241
elementClass: ElementClass;
242+
category: "color" | "segmentation";
241243
},
242244
>(
243245
specs: GpuSpecs,
@@ -262,15 +264,23 @@ function getRenderSupportedLayerCount<
262264
(specs.maxTextureCount - textureCountForSegmentation - lookupTextureCount) /
263265
maximumTextureCountForLayer,
264266
);
267+
268+
// Without any GPU restrictions, WK would be able to render all color layers
269+
// plus one segmentation layer. Use that as the upper layer count limit to avoid
270+
// compiling too complex shaders.
271+
const maximumLayerCount =
272+
Array.from(textureInformationPerLayer.keys()).filter((l) => l.category === "color").length +
273+
(hasSegmentation ? 1 : 0);
265274
return {
266-
maximumLayerCountToRender,
275+
maximumLayerCountToRender: Math.min(maximumLayerCountToRender, maximumLayerCount),
267276
maximumTextureCountForLayer,
268277
};
269278
}
270279

271280
export function computeDataTexturesSetup<
272281
Layer extends {
273282
elementClass: ElementClass;
283+
category: "color" | "segmentation";
274284
},
275285
>(
276286
specs: GpuSpecs,

frontend/javascripts/test/model/binary/data_rendering_logic.spec.ts

+45-54
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import _ from "lodash";
12
import {
23
calculateTextureSizeAndCountForLayer,
34
computeDataTexturesSetup,
@@ -25,6 +26,34 @@ const grayscaleElementClass = "uint8";
2526
const volumeByteCount = 4;
2627
const volumeElementClass = "uint32";
2728

29+
/*
30+
* The current rendering logic in WK only allows
31+
* as many layers as necessary. This is done to avoid
32+
* that the shaders are compiled for N layers even though
33+
* N layers will never be rendered at the same time (because
34+
* only one segmentation layer can be rendered at a time).
35+
* For that reason, testing the specs has to be done with a
36+
* sufficiently large amount of layers. To achieve this,
37+
* the helper function createLayers is used.
38+
*/
39+
40+
const createGrayscaleLayer = () => ({
41+
byteCount: grayscaleByteCount,
42+
elementClass: grayscaleElementClass,
43+
category: "color",
44+
});
45+
const createVolumeLayer = () => ({
46+
byteCount: volumeByteCount,
47+
elementClass: volumeElementClass,
48+
category: "segmentation",
49+
});
50+
51+
function createLayers(grayscaleCount: number, volumeCount: number) {
52+
const grayscaleLayers = _.range(0, grayscaleCount).map(() => createGrayscaleLayer());
53+
const volumeLayers = _.range(0, volumeCount).map(() => createVolumeLayer());
54+
return grayscaleLayers.concat(volumeLayers);
55+
}
56+
2857
test("calculateTextureSizeAndCountForLayer: grayscale data + minSpecs", (t) => {
2958
const { textureSize, textureCount } = calculateTextureSizeAndCountForLayer(
3059
minSpecs,
@@ -90,24 +119,8 @@ test("calculateTextureSizeAndCountForLayer: color data + betterSpecs", (t) => {
90119
t.is(textureSize, midSpecs.supportedTextureSize);
91120
t.is(textureCount, 1);
92121
});
93-
const grayscaleLayer1 = {
94-
byteCount: grayscaleByteCount,
95-
elementClass: grayscaleElementClass,
96-
};
97-
const grayscaleLayer2 = {
98-
byteCount: grayscaleByteCount,
99-
elementClass: grayscaleElementClass,
100-
};
101-
const grayscaleLayer3 = {
102-
byteCount: grayscaleByteCount,
103-
elementClass: grayscaleElementClass,
104-
};
105-
const volumeLayer1 = {
106-
byteCount: volumeByteCount,
107-
elementClass: volumeElementClass,
108-
};
109122

110-
type Layer = typeof grayscaleLayer1;
123+
type Layer = ReturnType<typeof createGrayscaleLayer>;
111124

112125
const getByteCount = (layer: Layer) => layer.byteCount;
113126

@@ -123,7 +136,7 @@ function computeDataTexturesSetupCurried(spec: typeof minSpecs, hasSegmentation:
123136
return (layers: Layer[]) =>
124137
computeDataTexturesSetup(
125138
spec,
126-
layers as { elementClass: ElementClass }[],
139+
layers as { elementClass: ElementClass; category: "color" | "segmentation" }[],
127140
getByteCount as any,
128141
hasSegmentation,
129142
DEFAULT_REQUIRED_BUCKET_CAPACITY,
@@ -137,59 +150,37 @@ test("Basic support (no segmentation): all specs", (t) => {
137150
[midSpecs, 15],
138151
[betterSpecs, 31],
139152
];
153+
const hundredGrayscaleLayers = createLayers(100, 0);
140154
for (const [spec, expectedLayerCount] of specs) {
141155
const computeDataTexturesSetupPartial = computeDataTexturesSetupCurried(spec, false);
142-
testSupportFlags(t, computeDataTexturesSetupPartial([grayscaleLayer1]), expectedLayerCount);
143156
testSupportFlags(
144157
t,
145-
computeDataTexturesSetupPartial([grayscaleLayer1, grayscaleLayer2]),
158+
computeDataTexturesSetupPartial(hundredGrayscaleLayers),
146159
expectedLayerCount,
147160
);
148161
testSupportFlags(
149162
t,
150-
computeDataTexturesSetupPartial([grayscaleLayer1, grayscaleLayer2, grayscaleLayer3]),
163+
computeDataTexturesSetupPartial(hundredGrayscaleLayers),
164+
expectedLayerCount,
165+
);
166+
testSupportFlags(
167+
t,
168+
computeDataTexturesSetupPartial(hundredGrayscaleLayers),
151169
expectedLayerCount,
152170
);
153171
}
154172
});
155173

156174
test("Basic support + volume: min specs", (t) => {
157175
const computeDataTexturesSetupPartial = computeDataTexturesSetupCurried(minSpecs, true);
158-
testSupportFlags(t, computeDataTexturesSetupPartial([grayscaleLayer1, grayscaleLayer2]), 4);
159-
testSupportFlags(t, computeDataTexturesSetupPartial([grayscaleLayer1, volumeLayer1]), 1);
160-
testSupportFlags(
161-
t,
162-
computeDataTexturesSetupPartial([grayscaleLayer1, grayscaleLayer2, volumeLayer1]),
163-
1,
164-
);
165-
testSupportFlags(
166-
t,
167-
computeDataTexturesSetupPartial([
168-
grayscaleLayer1,
169-
grayscaleLayer2,
170-
grayscaleLayer3,
171-
volumeLayer1,
172-
]),
173-
1,
174-
);
176+
testSupportFlags(t, computeDataTexturesSetupPartial(createLayers(10, 0)), 4);
177+
testSupportFlags(t, computeDataTexturesSetupPartial(createLayers(10, 1)), 1);
178+
testSupportFlags(t, computeDataTexturesSetupPartial(createLayers(10, 1)), 1);
179+
testSupportFlags(t, computeDataTexturesSetupPartial(createLayers(10, 1)), 1);
175180
});
176181

177182
test("Basic support + volume: mid specs", (t) => {
178183
const computeDataTexturesSetupPartial = computeDataTexturesSetupCurried(midSpecs, true);
179-
testSupportFlags(t, computeDataTexturesSetupPartial([grayscaleLayer1, volumeLayer1]), 12);
180-
testSupportFlags(
181-
t,
182-
computeDataTexturesSetupPartial([grayscaleLayer1, grayscaleLayer2, volumeLayer1]),
183-
12,
184-
);
185-
testSupportFlags(
186-
t,
187-
computeDataTexturesSetupPartial([
188-
grayscaleLayer1,
189-
grayscaleLayer2,
190-
grayscaleLayer3,
191-
volumeLayer1,
192-
]),
193-
12,
194-
);
184+
testSupportFlags(t, computeDataTexturesSetupPartial(createLayers(20, 1)), 12);
185+
testSupportFlags(t, computeDataTexturesSetupPartial(createLayers(5, 1)), 6);
195186
});

0 commit comments

Comments
 (0)