CanvasContext
A CanvasContext
holds a connection between a GPU Device
and canvas, (either an HTML <canvas />
element, aka HTMLCanvasELement
, or an OffscreenCanvas
).
- A
CanvasContext
enables the application to render into the associated canvas, by acting as a source ofFramebuffer
s with specialTexture
color attachments that are copied to the screen at the end of aRenderPass
. - It handles canvas resizing, making sure the returned
Framebuffer
s correspond to the current size of the canvas. - It also provides support for device pixel ratios (mapping between device pixels and CSS pixels)
Canvas Size Management
While an OffscreenCanvas
only has one size, HTMLCanvasElements
effectively has three different sizes:
- The CSS size, being the size in "logical units" of the canvas
- The pixel size, being the exact number of pixels covered by the canvas
- The drawing buffer size, being the size of the texture created to render into the canvas.
Notes:
- If the drawing buffer size doesn't exactly match the pixel size, undesired effects like moire patterns can result.The
CanvasContext
pixelWidth and pixelHeight members tracks the exact pixel size (called the "device pixel content box" in browser APIs) is surprisingly hard.
Canvas Monitoring
For HTMLCanvasElements
the CanvasContext
will monitor changes to the underlying canvas and call callbacks on the associated Device
, see:
DeviceProps.onResize
- called if the size of the "device pixel content box" changes.DeviceProps.onVisibilityChange
- called if the visibility of the canvas changes (window is closed or occluded).DeviceProps.onDevicePixelRatioChange
- called if the DPR changes (perhaps by moving the window to another screen or zooming the browser)
Usage
The luma.gl API is designed to allow a Device
to create multiple associated CanvasContext
s (or none, if only used for compute).
const device = await luma.createDevice(...);
const canvasContext1 = device.createCanvasContext(...);
const canvasContext2 = device.createCanvasContext(...);
However this is only supported on WebGPU. A WebGL Device
always has exactly one CanvasContext
that must be created when the device is created, and a WebGL device can only render into that single canvas. (This is a fundamental limitation of the WebGL API, outside of luma.gl's control).
Because of this, the Device
class provides a DeviceProps.createCanvasContext
property that creates a default CanvasContext
:
const device = await luma.createDevice({createCanvasContext: true});
const canvasContext = device.getDefaultCanvasContext()
The application can also provide properties for the default CanvasContext
:
const device = await luma.createDevice({createCanvasContext: {width, height}}); // Creates a new HTML canvas and adds it to document.body.
const canvasContext = device.getDefaultCanvasContext()
A CanvasContext
can be associated with an existing canvas:
const device = await luma.createDevice({createCanvasContext: {canvas: document.getElementById('canvas-id')}}); // Creates a new HTML canvas and adds it to document.body.
const canvasContext = device.getDefaultCanvasContext()
Use a device's default canvas context to render into the associated canvas
const renderPass = device.beginRenderPass({});
This is equivalent to
const renderPass = device.beginRenderPass({
framebuffer: device.getDefaultCanvasContext().getFramebuffer()
});
Rendering into additional canvas contexts (WebGPU only):
const newCanvasContext = device.createCanvasContext({canvas: ...});
const renderPass = device.beginRenderPass({
framebuffer: newCanvasContext.getFramebuffer()
});
On high-DPI screens, the number of pixels in a canvas can be a multiple of the "CSS size" reported by HTMLCanvasElement. Because of this, luma.gl allows the resolution of the textures returned by canvasContext.getFramebuffer
to be controlled. The CanvasContextProps.useDevicePixels
prop if set to true
, multiples the canvas HTML size with the system device pixel ratio. This prop can also a custom ratio (number
), as well. This allows setting the target texture size to higher or lower resolutions that indicated by an HMTLCanvasElements CSS width and height, to ensure that screen renderings use the maximum resolution of the device (at the cost of using more GPU memory).
const newCanvasContext = device.createCanvasContext({canvas: ..., useDevicePixels: true});
Mote that when using high value (usually more than device pixel ratio), it is possible it can get clamped down outside of luma.gl's control due to system memory limitation, in such cases a warning will be logged to the browser console.
The CanvasContext
also provides methods for converting between device and CSS pixels, e.g
canvasContext.getDevicePixelResolution()
Types
CanvasContextProps
Property | Type | |
---|---|---|
width? | number | Width in pixels of the canvas (if canvas is not supplied) |
height? | number | Height in pixels of the canvas (if canvas is not supplied) |
canvas? | HTMLCanvasElement | OffscreenCanvas | string | A new canvas will be created if not supplied. |
container? | HTMLElement | Parent DOM element for new canvas. Defaults to first child of document.body |
useDevicePixels? | boolean | number | Device pixels scale factor (true uses browser DPI) |
autoResize? | boolean | Whether to track resizes |
visible? | boolean | Visibility (only used if new canvas is created). |
alphaMode?: string | 'opaque' | 'opaque' | 'premultiplied' . See alphaMode. |
colorSpace?: 'string | 'srgb' | 'srgb' | 'display-p3' . See colorSpace. |
Fields
canvas: HMTLCanvas | OffscreenCanvas
initialized: Promise<void>
A promise that resolves when the CanvasContext
been able to obtain its true pixel size.
isInitialized: boolean
Becomes true
once the CanvasContext
been able to obtain its true pixel size.
useDevicePixels: boolean | number
Whether the framebuffer backing this canvas context is sized using device pixels.
false
- Framebuffer is sized according to CSS pixel size.true
- Framebuffer is sized according to the device pixel ratio reported by the browser.number
- Framebuffer is sized according to the provided ratio.
Methods
constructor
A CanvasContext
should not be constructed directly. Default canvas contexts are created when instantiating a WebGPUDevice
or a WebGLDevice
by supplying the canvasContext
property, and can be accessed through the device.canvasContext
field. Additional canvas contexts can be explicitly created through WebGPUDevice.createCanvasContext(...)
.
On Device
instances that support it (see remarks below) additional canvas contexts are created using device.createCanvasContext()
. Depending on options passed, this either:
- creates a new canvas element with the specified properties,
- or attaches the context to an existing canvas element
getCurrentFramebuffer(): Framebuffer
Returns a framebuffer with properly resized current 'swap chain' textures. Rendering to this framebuffer will update the canvas associated with that CanvasContext
. Note that a new Framebuffer
must be requested on every redraw cycle.
getCSSSize(): [number, number]
Returns the size in logical CSS units. This is useful when mapping DOM events (mouse clicks etc) to the canvas, as their coordinates will be in CSS units.
Note: For an OffscreenCanvas
this function always returns the same value as getPixelSize()
getPixelSize(): [number, number]
Returns the size in pixels required to cover the canvas at the current device pixel resolution. Note that this value is just informational, the render buffer can be set to any value independently of this size.
getRenderBufferSize(): [number, number]
If props.autoResize
is true, then this value will always match getPixelSize()
Note: For an OffscreenCanvas
this function always returns the same value as getPixelSize()
setDrawingBufferSize(size [number, number]): void
Resize the drawing surface. Usually called after the window has been resized.
canvasContext.setDrawingBufferSize([width: number, height: number]});
- width: New drawing surface width.
- height: New drawing surface height.
Note: if props.autoResize
is true, then automatic resizing is performed as size changes to the underlying canvas object are detected.