Skip to content

Commit 011eb33

Browse files
mofojeddsmmcken
andauthored
feat: Add option to disable WebGL rendering (#2134)
- Advanced Settings section in the Settings menu, with an option to disable WebGL - Read from the server config for default values - web.webgl: default setting for WebGL being enabled or not - web.webgl.editable: disallow the user from enabling WebGL - Contextual Help item to add more info - Tested by adding the following to the deephaven.prop config: ``` web.webgl=false web.webgl.editable=false client.configuration.list=java.version,deephaven.version,barrage.version,http.session.durationMs,file.separator,web.storage.layout.directory,web.storage.notebook.directory,web.webgl,web.webgl.editable ``` --------- Co-authored-by: Don <dsmmcken@gmail.com>
1 parent 232fb4d commit 011eb33

14 files changed

Lines changed: 212 additions & 17 deletions

File tree

packages/app-utils/src/storage/LocalWorkspaceStorage.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ export class LocalWorkspaceStorage implements WorkspaceStorage {
5959
defaultNotebookSettings: {
6060
isMinimapEnabled: false,
6161
},
62+
webgl: true,
63+
webglEditable: true,
6264
};
6365
const serverSettings = {
6466
defaultDateTimeFormat: serverConfigValues?.get('dateTimeFormat'),
@@ -109,6 +111,14 @@ export class LocalWorkspaceStorage implements WorkspaceStorage {
109111
) as boolean,
110112
}
111113
: undefined,
114+
webgl: LocalWorkspaceStorage.getBooleanServerConfig(
115+
serverConfigValues,
116+
'web.webgl'
117+
),
118+
webglEditable: LocalWorkspaceStorage.getBooleanServerConfig(
119+
serverConfigValues,
120+
'web.webgl.editable'
121+
),
112122
};
113123

114124
const keys = Object.keys(serverSettings) as Array<keyof typeof settings>;

packages/chart/src/Chart.tsx

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,23 +41,29 @@ import useChartTheme from './useChartTheme';
4141

4242
const log = Log.module('Chart');
4343

44-
type FormatterSettings = ColumnFormatSettings &
44+
type ChartSettings = ColumnFormatSettings &
4545
DateTimeFormatSettings & {
4646
decimalFormatOptions?: DecimalColumnFormatterOptions;
4747
integerFormatOptions?: IntegerColumnFormatterOptions;
48+
webgl?: boolean;
4849
};
4950

5051
interface ChartProps {
5152
model: ChartModel;
5253
theme: ChartTheme;
53-
settings: FormatterSettings;
54+
55+
/** User settings that are relevant to the chart, e.g. formatter settings */
56+
settings: ChartSettings;
57+
5458
isActive: boolean;
5559
Plotly: typeof Plotly;
5660
containerRef?: React.RefObject<HTMLDivElement>;
5761
onDisconnect: () => void;
5862
onReconnect: () => void;
5963
onUpdate: (obj: { isLoading: boolean }) => void;
6064
onError: (error: Error) => void;
65+
66+
/** Called when the settings for the ChartModel are changed */
6167
onSettingsChanged: (settings: Partial<ChartModelSettings>) => void;
6268
}
6369

@@ -91,6 +97,7 @@ class Chart extends Component<ChartProps, ChartState> {
9197
showTimeZone: false,
9298
showTSeparator: true,
9399
formatter: [],
100+
webgl: true,
94101
},
95102
Plotly,
96103
onDisconnect: (): void => undefined,
@@ -195,7 +202,7 @@ class Chart extends Component<ChartProps, ChartState> {
195202

196203
componentDidUpdate(prevProps: ChartProps): void {
197204
const { isActive, model, settings, theme } = this.props;
198-
this.updateFormatterSettings(settings as FormatterSettings);
205+
this.updateFormatterSettings(settings);
199206

200207
if (model !== prevProps.model) {
201208
this.unsubscribe(prevProps.model);
@@ -239,6 +246,8 @@ class Chart extends Component<ChartProps, ChartState> {
239246

240247
integerFormatOptions: IntegerColumnFormatterOptions;
241248

249+
webgl?: boolean;
250+
242251
rect?: DOMRect;
243252

244253
ranges?: unknown;
@@ -612,10 +621,10 @@ class Chart extends Component<ChartProps, ChartState> {
612621

613622
initFormatter(): void {
614623
const { settings } = this.props;
615-
this.updateFormatterSettings(settings as FormatterSettings);
624+
this.updateFormatterSettings(settings);
616625
}
617626

618-
updateFormatterSettings(settings: FormatterSettings): void {
627+
updateFormatterSettings(settings: ChartSettings): void {
619628
const columnFormats = FormatterUtils.getColumnFormats(settings);
620629
const dateTimeFormatterOptions =
621630
FormatterUtils.getDateTimeFormatterOptions(settings);
@@ -633,6 +642,11 @@ class Chart extends Component<ChartProps, ChartState> {
633642
this.integerFormatOptions = integerFormatOptions;
634643
this.updateFormatter();
635644
}
645+
646+
if (this.webgl !== settings.webgl) {
647+
this.webgl = settings.webgl;
648+
this.updateRenderOptions();
649+
}
636650
}
637651

638652
updateFormatter(): void {
@@ -647,6 +661,12 @@ class Chart extends Component<ChartProps, ChartState> {
647661
model.setFormatter(formatter);
648662
}
649663

664+
updateRenderOptions(): void {
665+
const { model } = this.props;
666+
const renderOptions = { webgl: this.webgl };
667+
model.setRenderOptions(renderOptions);
668+
}
669+
650670
updateDimensions(): void {
651671
const rect = this.getPlotRect();
652672
const { Plotly: PlotlyProp } = this.props;

packages/chart/src/ChartModel.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ import type { Layout, Data } from 'plotly.js';
77
import { FilterColumnMap, FilterMap } from './ChartUtils';
88

99
export type ChartEvent = CustomEvent;
10+
11+
export type RenderOptions = {
12+
/** Allow WebGL as an option. Defaults to `true`, explicitly set to `false` to disable. */
13+
webgl?: boolean;
14+
};
15+
1016
/**
1117
* Model for a Chart
1218
* All of these methods should return very quickly.
@@ -41,8 +47,11 @@ class ChartModel {
4147

4248
listeners: ((event: ChartEvent) => void)[];
4349

50+
/** Formatter settings for the chart, such as how to format dates and numbers */
4451
formatter?: Formatter;
4552

53+
renderOptions?: RenderOptions;
54+
4655
rect?: DOMRect;
4756

4857
isDownsamplingDisabled: boolean;
@@ -86,6 +95,14 @@ class ChartModel {
8695
this.formatter = formatter;
8796
}
8897

98+
/**
99+
* Set additional options for rendering the chart
100+
* @param renderOptions Options for rendering the chart
101+
*/
102+
setRenderOptions(renderOptions: RenderOptions): void {
103+
this.renderOptions = renderOptions;
104+
}
105+
89106
/**
90107
* Disable downsampling
91108
* @param isDownsamplingDisabled True if downsampling should be disabled

packages/chart/src/ChartUtils.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -681,17 +681,22 @@ class ChartUtils {
681681
* Converts the Iris plot style into a plotly chart type
682682
* @param plotStyle The plotStyle to use, see dh.plot.SeriesPlotStyle
683683
* @param isBusinessTime If the plot is using business time for an axis
684+
* @param allowWebGL If WebGL is allowedd
684685
*/
685686
getPlotlyChartType(
686687
plotStyle: DhType.plot.SeriesPlotStyle,
687-
isBusinessTime: boolean
688+
isBusinessTime: boolean,
689+
allowWebGL = true
688690
): PlotType | undefined {
689691
const { dh } = this;
690692
switch (plotStyle) {
691693
case dh.plot.SeriesPlotStyle.SCATTER:
692694
case dh.plot.SeriesPlotStyle.LINE:
693-
// scattergl mode is more performant, but doesn't support the rangebreaks we need for businessTime calendars
694-
return !isBusinessTime && IS_WEBGL_SUPPORTED ? 'scattergl' : 'scatter';
695+
// scattergl mode is more performant (usually), but doesn't support the rangebreaks we need for businessTime calendars
696+
// In some cases, WebGL is less performant (like in virtual desktop environments), so we also allow the option of the user explicitly disabling it even if it's supported
697+
return !isBusinessTime && IS_WEBGL_SUPPORTED && allowWebGL
698+
? 'scattergl'
699+
: 'scatter';
695700
case dh.plot.SeriesPlotStyle.BAR:
696701
case dh.plot.SeriesPlotStyle.STACKED_BAR:
697702
return 'bar';
@@ -871,7 +876,8 @@ class ChartUtils {
871876
series: DhType.plot.Series,
872877
axisTypeMap: AxisTypeMap,
873878
seriesVisibility: boolean | 'legendonly',
874-
showLegend: boolean | null = null
879+
showLegend: boolean | null = null,
880+
allowWebGL = true
875881
): Partial<PlotData> {
876882
const {
877883
name,
@@ -888,7 +894,7 @@ class ChartUtils {
888894
const isBusinessTime = sources.some(
889895
source => source.axis?.businessCalendar
890896
);
891-
const type = this.getChartType(plotStyle, isBusinessTime);
897+
const type = this.getChartType(plotStyle, isBusinessTime, allowWebGL);
892898
const mode = this.getPlotlyChartMode(
893899
plotStyle,
894900
isLinesVisible ?? undefined,
@@ -1019,7 +1025,8 @@ class ChartUtils {
10191025

10201026
getChartType(
10211027
plotStyle: DhType.plot.SeriesPlotStyle,
1022-
isBusinessTime: boolean
1028+
isBusinessTime: boolean,
1029+
allowWebGL = true
10231030
): PlotType | undefined {
10241031
const { dh } = this;
10251032
switch (plotStyle) {
@@ -1028,7 +1035,7 @@ class ChartUtils {
10281035
// plot.ly to calculate the bins and sum values, just convert it to a bar chart
10291036
return 'bar';
10301037
default:
1031-
return this.getPlotlyChartType(plotStyle, isBusinessTime);
1038+
return this.getPlotlyChartType(plotStyle, isBusinessTime, allowWebGL);
10321039
}
10331040
}
10341041

packages/chart/src/FigureChartModel.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import type {
1717
DateTimeColumnFormatter,
1818
Formatter,
1919
} from '@deephaven/jsapi-utils';
20-
import ChartModel, { ChartEvent } from './ChartModel';
20+
import ChartModel, { ChartEvent, RenderOptions } from './ChartModel';
2121
import ChartUtils, {
2222
AxisTypeMap,
2323
ChartModelSettings,
@@ -219,7 +219,8 @@ class FigureChartModel extends ChartModel {
219219
series,
220220
axisTypeMap,
221221
ChartUtils.getSeriesVisibility(series.name, this.settings),
222-
showLegend
222+
showLegend,
223+
this.renderOptions?.webgl ?? true
223224
);
224225

225226
this.seriesDataMap.set(series.name, seriesData);
@@ -535,6 +536,13 @@ class FigureChartModel extends ChartModel {
535536
this.resubscribe();
536537
}
537538

539+
setRenderOptions(renderOptions: RenderOptions): void {
540+
super.setRenderOptions(renderOptions);
541+
542+
// Reset all the series to re-render them with the correct rendering options
543+
this.initAllSeries();
544+
}
545+
538546
setDownsamplingDisabled(isDownsamplingDisabled: boolean): void {
539547
super.setDownsamplingDisabled(isDownsamplingDisabled);
540548

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React, { useCallback } from 'react';
2+
import { useDispatch } from 'react-redux';
3+
import {
4+
Content,
5+
ContextualHelp,
6+
Heading,
7+
Switch,
8+
Text,
9+
} from '@deephaven/components';
10+
import { getWebGL, getWebGLEditable, updateSettings } from '@deephaven/redux';
11+
import { useAppSelector } from '@deephaven/dashboard';
12+
13+
function AdvancedSectionContent(): JSX.Element {
14+
const dispatch = useDispatch();
15+
const webgl = useAppSelector(getWebGL);
16+
const webglEditable = useAppSelector(getWebGLEditable);
17+
18+
const handleWebglChange = useCallback(
19+
newValue => {
20+
dispatch(updateSettings({ webgl: newValue }));
21+
},
22+
[dispatch]
23+
);
24+
25+
const helpText = webglEditable ? (
26+
<Text>
27+
WebGL in most cases has significant performance improvements, particularly
28+
when plotting large datasets. However, in some environments (such as
29+
remote desktop environments), WebGL may not use hardware acceleration and
30+
have degraded performance. If you are experiencing issues with WebGL, you
31+
can disable it here.
32+
</Text>
33+
) : (
34+
<Text>
35+
WebGL is disabled by your system administrator. Contact your system
36+
administrator to enable.
37+
</Text>
38+
);
39+
40+
return (
41+
<>
42+
<div className="app-settings-menu-description">
43+
Advanced settings here. Be careful!
44+
</div>
45+
46+
<div className="form-row mb-3 pl-1">
47+
<Switch
48+
isSelected={webgl}
49+
isDisabled={!webglEditable}
50+
onChange={handleWebglChange}
51+
marginEnd="size-0"
52+
>
53+
Enable WebGL
54+
</Switch>
55+
<ContextualHelp variant="info" marginTop="size-50">
56+
<Heading>Enable WebGL</Heading>
57+
<Content>
58+
<Text>{helpText}</Text>
59+
</Content>
60+
</ContextualHelp>
61+
</div>
62+
</>
63+
);
64+
}
65+
66+
export default AdvancedSectionContent;

packages/code-studio/src/settings/SettingsMenu.tsx

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
vsPaintcan,
1010
dhUserIncognito,
1111
dhUser,
12+
vsTools,
1213
} from '@deephaven/icons';
1314
import {
1415
Button,
@@ -38,6 +39,7 @@ import {
3839
getFormattedPluginInfo,
3940
getFormattedVersionInfo,
4041
} from './SettingsUtils';
42+
import AdvancedSectionContent from './AdvancedSectionContent';
4143

4244
interface SettingsMenuProps {
4345
serverConfigValues: ServerConfigValues;
@@ -68,6 +70,8 @@ export class SettingsMenu extends Component<
6870

6971
static THEME_SECTION_KEY = 'SettingsMenu.theme';
7072

73+
static ADVANCED_SECTION_KEY = 'SettingsMenu.advanced';
74+
7175
static focusFirstInputInContainer(container: HTMLDivElement | null): void {
7276
const input = container?.querySelector('input, select, textarea');
7377
if (input) {
@@ -289,14 +293,37 @@ export class SettingsMenu extends Component<
289293
)}
290294
title={
291295
<>
292-
<FontAwesomeIcon icon={vsRecordKeys} transform="grow-2" />{' '}
296+
<FontAwesomeIcon
297+
icon={vsRecordKeys}
298+
transform="grow-2"
299+
className="mr-2"
300+
/>
293301
Keyboard Shortcuts
294302
</>
295303
}
296304
onToggle={this.handleSectionToggle}
297305
>
298306
<ShortcutSectionContent />
299307
</SettingsMenuSection>
308+
<SettingsMenuSection
309+
sectionKey={SettingsMenu.ADVANCED_SECTION_KEY}
310+
isExpanded={this.isSectionExpanded(
311+
SettingsMenu.ADVANCED_SECTION_KEY
312+
)}
313+
title={
314+
<>
315+
<FontAwesomeIcon
316+
icon={vsTools}
317+
transform="grow-4"
318+
className="mr-2"
319+
/>
320+
Advanced
321+
</>
322+
}
323+
onToggle={this.handleSectionToggle}
324+
>
325+
<AdvancedSectionContent />
326+
</SettingsMenuSection>
300327

301328
<div className="app-settings-footer">
302329
<div className="app-settings-footer-section">

packages/components/src/theme/theme-dark/theme-dark-components.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,4 +166,7 @@
166166
var(--dh-color-visual-gray) 5%,
167167
transparent
168168
);
169+
170+
/* Switch */
171+
--dh-toggle-switch-bg: var(--dh-color-gray-500);
169172
}

0 commit comments

Comments
 (0)