Skip to content

Commit

Permalink
Add contextAttributes map option (#5196)
Browse files Browse the repository at this point in the history
* Add contextAttributes map option

* Add changelog entry

* Use default powerPreference on CI

* Use default powerPreference on CI

* Address comments

* Improve Changelog comment

* Fix tests
  • Loading branch information
ibesora authored Dec 13, 2024
1 parent 2e22403 commit f1a70c0
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### ✨ Features and improvements
- Add support for projection type expression as part of a refactoring of the transfrom and projection classes ([#5139](https://github.com/maplibre/maplibre-gl-js/pull/5139))
- ⚠️ Support setting WebGL context options on map creation ([#5196](https://github.com/maplibre/maplibre-gl-js/pull/5196)). Previously supported WebGL context options like `antialias`, `preserveDrawingBuffer` and `failIfMajorPerformanceCaveat` must now be defined inside the `canvasContextAttributes` object on `MapOptions`.
- _...Add new stuff here..._

### 🐞 Bug fixes
Expand Down
50 changes: 23 additions & 27 deletions src/ui/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,21 +113,11 @@ export type MapOptions = {
*/
logoPosition?: ControlPosition;
/**
* If `true`, map creation will fail if the performance of MapLibre GL JS would be dramatically worse than expected
* (i.e. a software renderer would be used).
* @defaultValue false
* Set of WebGLContextAttributes that are applied to the WebGL context of the map.
* See https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext for more details.
* @defaultValue antialias: false, powerPreference: 'high-performance', preserveDrawingBuffer: false, failIfMajorPerformanceCaveat: false, desynchronized: false
*/
failIfMajorPerformanceCaveat?: boolean;
/**
* If `true`, the map's canvas can be exported to a PNG using `map.getCanvas().toDataURL()`. This is `false` by default as a performance optimization.
* @defaultValue false
*/
preserveDrawingBuffer?: boolean;
/**
* If `true`, the gl context will be created with MSAA antialiasing, which can be useful for antialiasing custom layers.
* Disabled by default as a performance optimization.
*/
antialias?: boolean;
canvasContextAttributes?: WebGLContextAttributes;
/**
* If `false`, the map won't attempt to re-request tiles once they expire per their HTTP `cacheControl`/`expires` headers.
* @defaultValue true
Expand Down Expand Up @@ -391,10 +381,16 @@ const defaultOptions: Readonly<Partial<MapOptions>> = {
bearingSnap: 7,
attributionControl: defaultAttributionControlOptions,
maplibreLogo: false,
failIfMajorPerformanceCaveat: false,
preserveDrawingBuffer: false,
refreshExpiredTiles: true,

canvasContextAttributes: {
antialias: false,
preserveDrawingBuffer: false,
powerPreference: 'high-performance',
failIfMajorPerformanceCaveat: false,
desynchronized: false
},

scrollZoom: true,
minZoom: defaultMinZoom,
maxZoom: defaultMaxZoom,
Expand Down Expand Up @@ -498,9 +494,7 @@ export class Map extends Camera {
_fullyLoaded: boolean;
_trackResize: boolean;
_resizeObserver: ResizeObserver;
_preserveDrawingBuffer: boolean;
_failIfMajorPerformanceCaveat: boolean;
_antialias: boolean;
_canvasContextAttributes: WebGLContextAttributes;
_refreshExpiredTiles: boolean;
_hash: Hash;
_delegatedListeners: Record<string, DelegatedListener[]>;
Expand Down Expand Up @@ -593,7 +587,10 @@ export class Map extends Camera {
constructor(options: MapOptions) {
PerformanceUtils.mark(PerformanceMarkers.create);

const resolvedOptions = {...defaultOptions, ...options} as CompleteMapOptions;
const resolvedOptions = {...defaultOptions, ...options, canvasContextAttributes: {
...defaultOptions.canvasContextAttributes,
...options.canvasContextAttributes
}} as CompleteMapOptions;

if (resolvedOptions.minZoom != null && resolvedOptions.maxZoom != null && resolvedOptions.minZoom > resolvedOptions.maxZoom) {
throw new Error('maxZoom must be greater than or equal to minZoom');
Expand Down Expand Up @@ -637,9 +634,7 @@ export class Map extends Camera {
this._interactive = resolvedOptions.interactive;
this._maxTileCacheSize = resolvedOptions.maxTileCacheSize;
this._maxTileCacheZoomLevels = resolvedOptions.maxTileCacheZoomLevels;
this._failIfMajorPerformanceCaveat = resolvedOptions.failIfMajorPerformanceCaveat === true;
this._preserveDrawingBuffer = resolvedOptions.preserveDrawingBuffer === true;
this._antialias = resolvedOptions.antialias === true;
this._canvasContextAttributes = {...resolvedOptions.canvasContextAttributes};
this._trackResize = resolvedOptions.trackResize === true;
this._bearingSnap = resolvedOptions.bearingSnap;
this._centerClampedToGround = resolvedOptions.centerClampedToGround;
Expand Down Expand Up @@ -3038,13 +3033,14 @@ export class Map extends Camera {

_setupPainter() {

// Maplibre WebGL context requires alpha, depth and stencil buffers. It also forces premultipliedAlpha: true.
// We use the values provided in the map constructor for the rest of context attributes
const attributes = {
...this._canvasContextAttributes,
alpha: true,
stencil: true,
depth: true,
failIfMajorPerformanceCaveat: this._failIfMajorPerformanceCaveat,
preserveDrawingBuffer: this._preserveDrawingBuffer,
antialias: this._antialias || false
stencil: true,
premultipliedAlpha: true
};

let webglcontextcreationerrorDetailObject: any = null;
Expand Down
43 changes: 43 additions & 0 deletions src/ui/map_tests/map_canvas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,46 @@ describe('Max Canvas Size option', () => {
expect(map.getCanvas().height).toBe(3072);
});
});

describe('WebGLContextAttributes options', () => {
test('Optional values can be set correctly', () => {
const container = window.document.createElement('div');
const canvasContextAttributes = {
antialias: true,
preserveDrawingBuffer: true,
powerPreference: 'default',
failIfMajorPerformanceCaveat: true,
desynchronized: true,
}
Object.defineProperty(container, 'clientWidth', {value: 2048});
Object.defineProperty(container, 'clientHeight', {value: 2048});
const map = createMap({container, canvasContextAttributes});
const gl = map.painter.context.gl;
const mapContextAttributes = gl.getContextAttributes();
expect(mapContextAttributes.antialias).toBe(canvasContextAttributes.antialias);
expect(mapContextAttributes.preserveDrawingBuffer).toBe(canvasContextAttributes.preserveDrawingBuffer);
expect(mapContextAttributes.powerPreference).toBe(canvasContextAttributes.powerPreference);
expect(mapContextAttributes.failIfMajorPerformanceCaveat).toBe(canvasContextAttributes.failIfMajorPerformanceCaveat);
expect(mapContextAttributes.desynchronized).toBe(canvasContextAttributes.desynchronized);
});

test('Required values cannot be set', () => {
const container = window.document.createElement('div');
const canvasContextAttributes = {
alpha: false,
depth: false,
stencil: false,
premultipliedAlpha: false,
}
Object.defineProperty(container, 'clientWidth', {value: 2048});
Object.defineProperty(container, 'clientHeight', {value: 2048});
const map = createMap({container, canvasContextAttributes});
const mapContextAttributes = map.painter.context.gl.getContextAttributes();
console.log(mapContextAttributes);
expect(mapContextAttributes.alpha).toBe(true);
expect(mapContextAttributes.depth).toBe(true);
expect(mapContextAttributes.stencil).toBe(true);
expect(mapContextAttributes.premultipliedAlpha).toBe(true);
});

});
2 changes: 1 addition & 1 deletion test/integration/query/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function performQueryOnFixture(fixture) {
interactive: false,
attributionControl: false,
pixelRatio: options.pixelRatio,
preserveDrawingBuffer: true,
canvasContextAttributes: {preserveDrawingBuffer: true, powerPreference: 'default'},
fadeDuration: options.fadeDuration || 0,
localIdeographFontFamily: options.localIdeographFontFamily || false,
crossSourceCollisions: typeof options.crossSourceCollisions === 'undefined' ? true : options.crossSourceCollisions
Expand Down
2 changes: 1 addition & 1 deletion test/integration/render/run_render_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ async function getImageFromStyle(styleForTest: StyleWithTestData, page: Page): P
attributionControl: false,
maxPitch: options.maxPitch,
pixelRatio: options.pixelRatio,
preserveDrawingBuffer: true,
canvasContextAttributes: {preserveDrawingBuffer: true, powerPreference: 'default'},
fadeDuration: options.fadeDuration || 0,
localIdeographFontFamily: options.localIdeographFontFamily || false as any,
crossSourceCollisions: typeof options.crossSourceCollisions === 'undefined' ? true : options.crossSourceCollisions,
Expand Down

0 comments on commit f1a70c0

Please sign in to comment.