Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 29 additions & 11 deletions modules/core/src/lib/deck-picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Copyright (c) vis.gl contributors

import {Buffer, Texture} from '@luma.gl/core';
import type {CanvasContext, Device} from '@luma.gl/core';
import type {CanvasContext, Device, PresentationContext} from '@luma.gl/core';
import PickLayersPass, {PickingColorDecoder} from '../passes/pick-layers-pass';
import log from '../utils/log';
import {getClosestObject, getUniqueObjects, PickedPixel} from './picking/query-object';
Expand Down Expand Up @@ -44,7 +44,7 @@ type PickOperationContext = {
layers: Layer[];
views: Record<string, View>;
viewports: Viewport[];
canvasContext?: CanvasContext;
canvasContext?: CanvasContext | PresentationContext;
onViewportActive: (viewport: Viewport) => void;
effects: Effect[];
};
Expand Down Expand Up @@ -158,7 +158,9 @@ export default class DeckPicker {
// Private

/** Ensures that picking framebuffer exists and matches the canvas size */
_resizeBuffer(canvasContext: CanvasContext = this.device.getDefaultCanvasContext()) {
_resizeBuffer(
canvasContext: CanvasContext | PresentationContext = this.device.getDefaultCanvasContext()
) {
// Create a frame buffer if not already available
if (!this.pickingFBO) {
const pickingColorTexture = this.device.createTexture({
Expand Down Expand Up @@ -282,7 +284,8 @@ export default class DeckPicker {
deviceRect,
cullRect,
effects,
pass: `picking:${mode}`
pass: `picking:${mode}`,
canvasContext
});

pickInfo = getClosestObject({
Expand Down Expand Up @@ -316,7 +319,8 @@ export default class DeckPicker {
},
cullRect,
effects,
pass: `picking:${mode}:z`
pass: `picking:${mode}:z`,
canvasContext
},
true
);
Expand Down Expand Up @@ -446,7 +450,8 @@ export default class DeckPicker {
deviceRect,
cullRect,
effects,
pass: `picking:${mode}`
pass: `picking:${mode}`,
canvasContext
});

pickInfo = getClosestObject({
Expand Down Expand Up @@ -480,7 +485,8 @@ export default class DeckPicker {
},
cullRect,
effects,
pass: `picking:${mode}:z`
pass: `picking:${mode}:z`,
canvasContext
},
true
);
Expand Down Expand Up @@ -588,7 +594,8 @@ export default class DeckPicker {
deviceRect,
cullRect: {x, y, width, height},
effects,
pass: `picking:${mode}`
pass: `picking:${mode}`,
canvasContext
});

const pickInfos = getUniqueObjects(pickedResult);
Expand Down Expand Up @@ -693,7 +700,8 @@ export default class DeckPicker {
deviceRect,
cullRect: {x, y, width, height},
effects,
pass: `picking:${mode}`
pass: `picking:${mode}`,
canvasContext
});

const pickInfos = getUniqueObjects(pickedResult);
Expand Down Expand Up @@ -751,6 +759,7 @@ export default class DeckPicker {
onViewportActive: (viewport: Viewport) => void;
cullRect?: Rect;
effects: Effect[];
canvasContext?: CanvasContext | PresentationContext;
}): Promise<{
pickedColors: Uint8Array;
decodePickingColor: PickingColorDecoder;
Expand All @@ -767,6 +776,7 @@ export default class DeckPicker {
onViewportActive: (viewport: Viewport) => void;
cullRect?: Rect;
effects: Effect[];
canvasContext?: CanvasContext | PresentationContext;
},
pickZ: true
): Promise<{
Expand All @@ -784,7 +794,8 @@ export default class DeckPicker {
deviceRect,
cullRect,
effects,
pass
pass,
canvasContext
}: {
deviceRect: Rect;
pass: string;
Expand All @@ -794,6 +805,7 @@ export default class DeckPicker {
onViewportActive: (viewport: Viewport) => void;
cullRect?: Rect;
effects: Effect[];
canvasContext?: CanvasContext | PresentationContext;
},
pickZ: boolean = false
): Promise<{
Expand All @@ -812,6 +824,7 @@ export default class DeckPicker {
cullRect,
effects,
pass,
canvasContext,
pickZ,
preRenderStats: {},
isPicking: true
Expand Down Expand Up @@ -921,6 +934,7 @@ export default class DeckPicker {
onViewportActive: (viewport: Viewport) => void;
cullRect?: Rect;
effects: Effect[];
canvasContext?: CanvasContext | PresentationContext;
}): {
pickedColors: Uint8Array;
decodePickingColor: PickingColorDecoder;
Expand All @@ -940,6 +954,7 @@ export default class DeckPicker {
onViewportActive: (viewport: Viewport) => void;
cullRect?: Rect;
effects: Effect[];
canvasContext?: CanvasContext | PresentationContext;
},
pickZ: true
): {
Expand All @@ -957,7 +972,8 @@ export default class DeckPicker {
deviceRect,
cullRect,
effects,
pass
pass,
canvasContext
}: {
deviceRect: Rect;
pass: string;
Expand All @@ -967,6 +983,7 @@ export default class DeckPicker {
onViewportActive: (viewport: Viewport) => void;
cullRect?: Rect;
effects: Effect[];
canvasContext?: CanvasContext | PresentationContext;
},
pickZ: boolean = false
): {
Expand All @@ -985,6 +1002,7 @@ export default class DeckPicker {
cullRect,
effects,
pass,
canvasContext,
pickZ,
preRenderStats: {},
isPicking: true
Expand Down
12 changes: 7 additions & 5 deletions modules/core/src/lib/deck-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors

import type {Device} from '@luma.gl/core';
import type {CanvasContext, Device, PresentationContext} from '@luma.gl/core';
import {Framebuffer} from '@luma.gl/core';
import debug from '../debug/index';
import DrawLayersPass from '../passes/draw-layers-pass';
Expand Down Expand Up @@ -65,6 +65,7 @@ export default class DeckRenderer {
onViewportActive: (viewport: Viewport) => void;
effects: Effect[];
target?: Framebuffer | null;
canvasContext?: CanvasContext | PresentationContext;
layerFilter?: LayerFilter;
clearStack?: boolean;
clearCanvas?: boolean;
Expand Down Expand Up @@ -146,13 +147,13 @@ export default class DeckRenderer {
}

if (this.lastPostProcessEffect) {
this._resizeRenderBuffers();
this._resizeRenderBuffers(opts.canvasContext);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Resolve post-process output against the selected canvas

When a non-default canvasContext is rendered with a built-in PostProcessEffect and the canvas target is null, this only sizes the intermediate buffers from the selected context. _postRender still gives the final effect opts.target, so PostProcessEffect/ScreenPass starts its final pass with a null framebuffer, which selects the device's default canvas rather than canvasContext.getCurrentFramebuffer(); the selected canvas receives no composited output while the default canvas is overwritten.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 0309acd. DeckRenderer._postRender now resolves the final output as opts.target ?? opts.canvasContext?.getCurrentFramebuffer() ?? opts.target, so the last post-processing effect renders to the selected canvas while preserving existing default-canvas behavior when no alternate context is supplied.

Regression coverage: DeckRenderer#post processing renders to the supplied canvas framebuffer exercises a two-effect chain and verifies the intermediate effect receives no target while the final effect receives the selected framebuffer. Revalidated with 14 affected rendering/picking tests passing.

}
}

private _resizeRenderBuffers() {
private _resizeRenderBuffers(canvasContext = this.device.canvasContext!) {
const {renderBuffers} = this;
const size = this.device.canvasContext!.getDrawingBufferSize();
const size = canvasContext.getDrawingBufferSize();
const [width, height] = size;
if (renderBuffers.length === 0) {
[0, 1].map(i => {
Expand All @@ -176,6 +177,7 @@ export default class DeckRenderer {

private _postRender(effects: Effect[], opts: LayersPassRenderOptions) {
const {renderBuffers} = this;
const target = opts.target ?? opts.canvasContext?.getCurrentFramebuffer() ?? opts.target;
const params: PostRenderOptions = {
...opts,
inputBuffer: renderBuffers[0],
Expand All @@ -185,7 +187,7 @@ export default class DeckRenderer {
if (effect.postRender) {
// If not the last post processing effect, unset the target so that
// it only renders between the swap buffers
params.target = effect.id === this.lastPostProcessEffect ? opts.target : undefined;
params.target = effect.id === this.lastPostProcessEffect ? target : undefined;
const buffer = effect.postRender(params);
// Buffer cannot be null if target is unset
params.inputBuffer = buffer!;
Expand Down
28 changes: 19 additions & 9 deletions modules/core/src/passes/layers-pass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
// Copyright (c) vis.gl contributors

import type {
CanvasContext,
Device,
Parameters,
PresentationContext,
RenderPassParameters,
RenderPipelineParameters
} from '@luma.gl/core';
Expand Down Expand Up @@ -36,6 +38,8 @@ const WEBGPU_DEFAULT_DRAW_PARAMETERS: RenderPipelineParameters = {
export type LayersPassRenderOptions = {
/** @deprecated TODO v9 recommend we rename this to framebuffer to minimize confusion */
target?: Framebuffer | null;
/** Canvas context that provides framebuffer dimensions and pixel conversion. */
canvasContext?: CanvasContext | PresentationContext;
isPicking?: boolean;
pass: string;
layers: Layer[];
Expand Down Expand Up @@ -87,7 +91,7 @@ export default class LayersPass extends Pass {
}

protected _render(options: LayersPassRenderOptions): RenderStats[] {
const canvasContext = this.device.canvasContext!;
const {canvasContext = this.device.canvasContext!} = options;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clear views on the selected canvas framebuffer

When canvasContext is a non-default presentation context and a View has clear enabled, _render resolves that context's framebuffer here, but _drawLayers still forwards only the original target (typically null or undefined) and _drawLayersInViewport opens its clear pass with framebuffer: target. The clear consequently runs against the device's default canvas rather than the framebuffer used by the outer render pass, leaving stale color/depth in the selected canvas and potentially clearing another canvas; pass the resolved framebuffer into the per-view clear.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 0309acd. LayersPass._render now forwards its resolved framebuffer into _drawLayers, so every per-view clear pass uses the same selected-canvas framebuffer even when the original target was null or undefined.

Regression coverage: LayersPass#uses the supplied canvas context for viewport and clear passes spies on both render-pass creations and asserts that both receive the selected framebuffer. Revalidated with 14 affected rendering/picking tests passing.

const framebuffer = options.target ?? canvasContext.getCurrentFramebuffer();
const [width, height] = canvasContext.getDrawingBufferSize();

Expand Down Expand Up @@ -115,7 +119,7 @@ export default class LayersPass extends Pass {
});

try {
return this._drawLayers(renderPass, options);
return this._drawLayers(renderPass, {...options, target: framebuffer});
} finally {
renderPass.end();
// TODO(ibgreen): WebGPU - submit may not be needed here but initial port had issues with out of render loop rendering
Expand All @@ -126,6 +130,7 @@ export default class LayersPass extends Pass {
/** Draw a list of layers in a list of viewports */
private _drawLayers(renderPass: RenderPass, options: LayersPassRenderOptions) {
const {
canvasContext = this.device.canvasContext!,
target,
shaderModuleProps,
viewports,
Expand Down Expand Up @@ -156,6 +161,7 @@ export default class LayersPass extends Pass {
renderPass,
{
target,
canvasContext,
shaderModuleProps,
viewport: subViewport,
view,
Expand Down Expand Up @@ -184,6 +190,7 @@ export default class LayersPass extends Pass {
cullRect,
views,
effects,
canvasContext = this.device.canvasContext!,
shaderModuleProps
}: LayersPassRenderOptions,
/** Internal flag, true if only used to determine whether each layer should be drawn */
Expand Down Expand Up @@ -223,6 +230,7 @@ export default class LayersPass extends Pass {
layer,
effects,
pass,
canvasContext,
shaderModuleProps
);
const defaultParams =
Expand Down Expand Up @@ -251,6 +259,7 @@ export default class LayersPass extends Pass {
shaderModuleProps: globalModuleParameters,
pass,
target,
canvasContext,
viewport,
view,
isPicking
Expand All @@ -259,13 +268,15 @@ export default class LayersPass extends Pass {
shaderModuleProps: Record<string, any>;
pass: string;
target?: Framebuffer | null;
canvasContext: CanvasContext | PresentationContext;
viewport: Viewport;
view?: View;
isPicking?: boolean;
},
drawLayerParams: DrawLayerParameters[]
): RenderStats {
const glViewport = getGLViewport(this.device, {
canvasContext,
shaderModuleProps: globalModuleParameters,
target,
viewport
Expand Down Expand Up @@ -431,10 +442,10 @@ export default class LayersPass extends Pass {
layer: Layer,
effects: Effect[] | undefined,
pass: string,
canvasContext: CanvasContext | PresentationContext,
overrides: any
): any {
// @ts-expect-error TODO - assuming WebGL context
const devicePixelRatio = this.device.canvasContext.cssToDeviceRatio();
const devicePixelRatio = canvasContext.cssToDeviceRatio();
const layerProps = layer.internalState?.propsInTransition || layer.props;

const shaderModuleProps = {
Expand Down Expand Up @@ -530,23 +541,22 @@ export function layerIndexResolver(
function getGLViewport(
device: Device,
{
canvasContext = device.canvasContext!,
shaderModuleProps,
target,
viewport
}: {
canvasContext?: CanvasContext | PresentationContext;
shaderModuleProps: any;
target?: Framebuffer | null;
viewport: Viewport;
}
): [number, number, number, number] {
const pixelRatio =
shaderModuleProps?.project?.devicePixelRatio ??
// @ts-expect-error TODO - assuming WebGL context
device.canvasContext.cssToDeviceRatio();
shaderModuleProps?.project?.devicePixelRatio ?? canvasContext.cssToDeviceRatio();

// Default framebuffer is used when writing to canvas
// @ts-expect-error TODO - assuming WebGL context
const [, drawingBufferHeight] = device.canvasContext.getDrawingBufferSize();
const [, drawingBufferHeight] = canvasContext.getDrawingBufferSize();
const height = target ? target.height : drawingBufferHeight;

// Convert viewport top-left CSS coordinates to bottom up WebGL coordinates
Expand Down
2 changes: 2 additions & 0 deletions modules/core/src/passes/pick-layers-pass.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export default class PickLayersPass extends LayersPass {
effects,
pass = 'picking',
pickZ,
canvasContext,
shaderModuleProps,
clearColor
}: PickLayersPassRenderOptions): {
Expand All @@ -99,6 +100,7 @@ export default class PickLayersPass extends LayersPass {
cullRect,
effects: effects?.filter(e => e.useInPicking),
pass,
canvasContext,
isPicking: true,
shaderModuleProps,
clearColor: clearColor ?? [0, 0, 0, 0],
Expand Down
Loading
Loading