From d90c2d6b88d6c677334993f70088a305f677768d Mon Sep 17 00:00:00 2001 From: "yuqi.pyq" Date: Fri, 30 Jun 2023 10:53:56 +0800 Subject: [PATCH] fix: make pick async to support impl in WebGPU #1400 --- __tests__/unit/camera/camera.spec.ts | 148 +++++++++ packages/g-lite/src/camera/Camera.ts | 22 +- packages/g-lite/src/plugins/EventPlugin.ts | 19 +- packages/g-lite/src/services/EventService.ts | 91 ++--- .../src/PickingPlugin.ts | 311 +++++++++--------- .../src/platform/interfaces.ts | 2 +- .../src/platform/Readback.ts | 4 +- .../src/platform/Readback.ts | 15 +- .../src/platform/interfaces.ts | 3 + 9 files changed, 382 insertions(+), 233 deletions(-) diff --git a/__tests__/unit/camera/camera.spec.ts b/__tests__/unit/camera/camera.spec.ts index 82de6b138..d108f7294 100644 --- a/__tests__/unit/camera/camera.spec.ts +++ b/__tests__/unit/camera/camera.spec.ts @@ -5,6 +5,7 @@ import { AdvancedCamera, CameraProjectionMode, Canvas, + ClipSpaceNearZ, } from '../../../packages/g/src'; expect.extend({ toBeDeepCloseTo, toMatchCloseTo }); @@ -127,6 +128,153 @@ describe('Camera', () => { expect(camera.getAzimuth()).toBe(0); }); + it('should create an orthoZO camera correctly', () => { + const width = 600; + const height = 500; + const camera = new AdvancedCamera(); + camera.clipSpaceNearZ = ClipSpaceNearZ.ZERO; + camera + .setPosition(width / 2, height / 2, 500) + .setFocalPoint(width / 2, height / 2, 0) + .setOrthographic( + width / -2, + width / 2, + height / -2, + height / 2, + 0.1, + 1000, + ); + + expect(camera.getProjectionMode()).toBe(CameraProjectionMode.ORTHOGRAPHIC); + expect(camera.getZoom()).toBe(1); + expect(camera.getFar()).toBe(1000); + expect(camera.getNear()).toBe(0.1); + expect(camera.getPosition()).toStrictEqual(vec3.fromValues(300, 250, 500)); + expect(camera.getFocalPoint()).toStrictEqual(vec3.fromValues(300, 250, 0)); + expect(camera.getDistance()).toBe(500); + + expect(camera.getViewTransform()).toStrictEqual( + mat4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -300, -250, -500, 1), + ); + expect(camera.getWorldTransform()).toStrictEqual( + mat4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 300, 250, 500, 1), + ); + + expect(camera.getPerspective()).toBeDeepCloseTo( + mat4.fromValues( + 0.0033333334140479565, + 0, + 0, + 0, + -0, + 0.004000000189989805, + -0, + -0, + 0, + 0, + -0.0020002000965178013, + 0, + -0, + 0, + -0.00010001000191550702, + 1, + ), + ); + }); + + it('should create an perspective camera correctly', () => { + const width = 600; + const height = 500; + const camera = new AdvancedCamera(); + camera + .setPosition(width / 2, height / 2, 500) + .setFocalPoint(width / 2, height / 2, 0) + .setPerspective(0.1, 1000, 45, width / height); + + expect(camera.getProjectionMode()).toBe(CameraProjectionMode.PERSPECTIVE); + expect(camera.getZoom()).toBe(1); + expect(camera.getFar()).toBe(1000); + expect(camera.getNear()).toBe(0.1); + expect(camera.getPosition()).toStrictEqual(vec3.fromValues(300, 250, 500)); + expect(camera.getFocalPoint()).toStrictEqual(vec3.fromValues(300, 250, 0)); + expect(camera.getDistance()).toBe(500); + + expect(camera.getViewTransform()).toStrictEqual( + mat4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -300, -250, -500, 1), + ); + expect(camera.getWorldTransform()).toStrictEqual( + mat4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 300, 250, 500, 1), + ); + + expect(camera.getPerspective()).toBeDeepCloseTo( + mat4.fromValues( + 2.0118446350097656, + 0, + 0, + 0, + -0, + -2.4142136573791504, + -0, + -0, + 0, + 0, + -1.0002000331878662, + -1, + -0, + 0, + -0.20002000033855438, + 0, + ), + ); + }); + + it('should create an perspectiveZO camera correctly', () => { + const width = 600; + const height = 500; + const camera = new AdvancedCamera(); + camera.clipSpaceNearZ = ClipSpaceNearZ.ZERO; + camera + .setPosition(width / 2, height / 2, 500) + .setFocalPoint(width / 2, height / 2, 0) + .setPerspective(0.1, 1000, 45, width / height); + + expect(camera.getProjectionMode()).toBe(CameraProjectionMode.PERSPECTIVE); + expect(camera.getZoom()).toBe(1); + expect(camera.getFar()).toBe(1000); + expect(camera.getNear()).toBe(0.1); + expect(camera.getPosition()).toStrictEqual(vec3.fromValues(300, 250, 500)); + expect(camera.getFocalPoint()).toStrictEqual(vec3.fromValues(300, 250, 0)); + expect(camera.getDistance()).toBe(500); + + expect(camera.getViewTransform()).toStrictEqual( + mat4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -300, -250, -500, 1), + ); + expect(camera.getWorldTransform()).toStrictEqual( + mat4.fromValues(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 300, 250, 500, 1), + ); + + expect(camera.getPerspective()).toBeDeepCloseTo( + mat4.fromValues( + 2.0118446350097656, + 0, + 0, + 0, + -0, + -2.4142136573791504, + -0, + -0, + 0, + 0, + -1.0002000331878662, + -1, + -0, + 0, + -0.10001000016927719, + 0, + ), + ); + }); + it('should setDistance correctly.', () => { const width = 600; const height = 500; diff --git a/packages/g-lite/src/camera/Camera.ts b/packages/g-lite/src/camera/Camera.ts index 5334fcfb5..b0955eda2 100644 --- a/packages/g-lite/src/camera/Camera.ts +++ b/packages/g-lite/src/camera/Camera.ts @@ -36,49 +36,51 @@ const MIN_DISTANCE = 0.0002; export class Camera implements ICamera { canvas: Canvas; - clipSpaceNearZ: ClipSpaceNearZ; + + /** + * Clip space near Z, default to range `[-1, 1]` + */ + clipSpaceNearZ = ClipSpaceNearZ.NEGATIVE_ONE; eventEmitter = new EventEmitter(); /** - * 相机矩阵 + * Matrix of camera */ protected matrix = mat4.create(); /** - * u 轴 + * u axis +X is right * @see http://learnwebgl.brown37.net/07_cameras/camera_introduction.html#a-camera-definition */ protected right = vec3.fromValues(1, 0, 0); /** - * v 轴 +Y is down + * v axis +Y is up */ protected up = vec3.fromValues(0, 1, 0); /** - * n 轴 +Z is inside + * n axis +Z is inside */ protected forward = vec3.fromValues(0, 0, 1); /** - * 相机位置 + * Position of camera. */ protected position = vec3.fromValues(0, 0, 1); /** - * 视点位置 + * Position of focal point. */ protected focalPoint = vec3.fromValues(0, 0, 0); /** - * 视点到相机位置的向量 - * focalPoint - position + * vector from focalPoint to position */ protected distanceVector = vec3.fromValues(0, 0, -1); /** - * 相机位置到视点距离 * length(focalPoint - position) */ protected distance = 1; diff --git a/packages/g-lite/src/plugins/EventPlugin.ts b/packages/g-lite/src/plugins/EventPlugin.ts index 3ae830171..3163eb24d 100644 --- a/packages/g-lite/src/plugins/EventPlugin.ts +++ b/packages/g-lite/src/plugins/EventPlugin.ts @@ -29,14 +29,17 @@ export class EventPlugin implements RenderingPlugin { const canvas = this.context.renderingContext.root.ownerDocument.defaultView; - this.context.eventService.setPickHandler((position: EventPosition) => { - const { picked } = this.context.renderingService.hooks.pickSync.call({ - position, - picked: [], - topmost: true, // we only concern the topmost element - }); - return picked[0] || null; - }); + this.context.eventService.setPickHandler( + async (position: EventPosition) => { + const { picked } = + await this.context.renderingService.hooks.pick.promise({ + position, + picked: [], + topmost: true, // we only concern the topmost element + }); + return picked[0] || null; + }, + ); renderingService.hooks.pointerWheel.tap( EventPlugin.tag, diff --git a/packages/g-lite/src/services/EventService.ts b/packages/g-lite/src/services/EventService.ts index ba4e141d0..ca665410d 100644 --- a/packages/g-lite/src/services/EventService.ts +++ b/packages/g-lite/src/services/EventService.ts @@ -18,7 +18,7 @@ import { Point } from '../shapes'; import type { Cursor, EventPosition } from '../types'; import { isElement } from '../utils/dom'; -type Picker = (position: EventPosition) => IEventTarget | null; +type Picker = (position: EventPosition) => Promise; type TrackingData = { pressTargetsByButton: Record; clicksByButton: Record< @@ -180,12 +180,8 @@ export class EventService { } } - onPointerDown = (from: FederatedPointerEvent) => { - // if (!(from instanceof FederatedPointerEvent)) { - // return; - // } - - const e = this.createPointerEvent(from); + onPointerDown = async (from: FederatedPointerEvent) => { + const e = await this.createPointerEvent(from); this.dispatchEvent(e, 'pointerdown'); @@ -203,13 +199,9 @@ export class EventService { this.freeEvent(e); }; - onPointerUp = (from: FederatedPointerEvent) => { - // if (!(from instanceof FederatedPointerEvent)) { - // return; - // } - + onPointerUp = async (from: FederatedPointerEvent) => { const now = performance.now(); - const e = this.createPointerEvent( + const e = await this.createPointerEvent( from, undefined, undefined, @@ -315,12 +307,8 @@ export class EventService { this.freeEvent(e); }; - onPointerMove = (from: FederatedPointerEvent) => { - // if (!(from instanceof FederatedPointerEvent)) { - // return; - // } - - const e = this.createPointerEvent( + onPointerMove = async (from: FederatedPointerEvent) => { + const e = await this.createPointerEvent( from, undefined, undefined, @@ -337,7 +325,7 @@ export class EventService { if (trackingData.overTargets && outTarget !== e.target) { // pointerout always occurs on the overTarget when the pointer hovers over another element. const outType = from.type === 'mousemove' ? 'mouseout' : 'pointerout'; - const outEvent = this.createPointerEvent( + const outEvent = await this.createPointerEvent( from, outType, outTarget || undefined, @@ -349,7 +337,7 @@ export class EventService { // If the pointer exits overTarget and its descendants, then a pointerleave event is also fired. This event // is dispatched to all ancestors that no longer capture the pointer. if (!e.composedPath().includes(outTarget!)) { - const leaveEvent = this.createPointerEvent( + const leaveEvent = await this.createPointerEvent( from, 'pointerleave', outTarget || undefined, @@ -451,11 +439,7 @@ export class EventService { this.freeEvent(e); }; - onPointerOut = (from: FederatedPointerEvent) => { - // if (!(from instanceof FederatedPointerEvent)) { - // return; - // } - + onPointerOut = async (from: FederatedPointerEvent) => { const trackingData = this.trackingData(from.pointerId); if (trackingData.overTargets) { @@ -464,7 +448,7 @@ export class EventService { const outTarget = this.findMountedTarget(trackingData.overTargets); // pointerout first - const outEvent = this.createPointerEvent( + const outEvent = await this.createPointerEvent( from, 'pointerout', outTarget || undefined, @@ -475,7 +459,7 @@ export class EventService { // pointerleave(s) are also dispatched b/c the pointer must've left rootTarget and its descendants to // get an upstream pointerout event (upstream events do not know rootTarget has descendants). - const leaveEvent = this.createPointerEvent( + const leaveEvent = await this.createPointerEvent( from, 'pointerleave', outTarget || undefined, @@ -509,13 +493,9 @@ export class EventService { this.cursor = null; }; - onPointerOver = (from: FederatedPointerEvent) => { - // if (!(from instanceof FederatedPointerEvent)) { - // return; - // } - + onPointerOver = async (from: FederatedPointerEvent) => { const trackingData = this.trackingData(from.pointerId); - const e = this.createPointerEvent(from); + const e = await this.createPointerEvent(from); const isMouse = e.pointerType === 'mouse' || e.pointerType === 'pen'; @@ -553,16 +533,12 @@ export class EventService { this.freeEvent(enterEvent); }; - onPointerUpOutside = (from: FederatedPointerEvent) => { - // if (!(from instanceof FederatedPointerEvent)) { - // return; - // } - + onPointerUpOutside = async (from: FederatedPointerEvent) => { const trackingData = this.trackingData(from.pointerId); const pressTarget = this.findMountedTarget( trackingData.pressTargetsByButton[from.button], ); - const e = this.createPointerEvent(from); + const e = await this.createPointerEvent(from); if (pressTarget) { let currentTarget: IEventTarget | null = pressTarget; @@ -592,27 +568,27 @@ export class EventService { this.freeEvent(e); }; - onWheel = (from: FederatedWheelEvent) => { + onWheel = async (from: FederatedWheelEvent) => { // if (!(from instanceof FederatedWheelEvent)) { // return; // } - const wheelEvent = this.createWheelEvent(from); + const wheelEvent = await this.createWheelEvent(from); this.dispatchEvent(wheelEvent); this.freeEvent(wheelEvent); }; - onClick = (from: FederatedPointerEvent) => { + onClick = async (from: FederatedPointerEvent) => { if (this.context.config.useNativeClickEvent) { - const e = this.createPointerEvent(from); + const e = await this.createPointerEvent(from); this.dispatchEvent(e); this.freeEvent(e); } }; - onPointerCancel = (from: FederatedPointerEvent) => { - const e = this.createPointerEvent( + onPointerCancel = async (from: FederatedPointerEvent) => { + const e = await this.createPointerEvent( from, undefined, undefined, @@ -707,7 +683,7 @@ export class EventService { return propagationPath; } - hitTest(position: EventPosition): IEventTarget | null { + async hitTest(position: EventPosition): Promise { const { viewportX, viewportY } = position; const { width, height } = this.context.config; // outside canvas @@ -721,7 +697,7 @@ export class EventService { } return ( - this.pickHandler(position) || + (await this.pickHandler(position)) || this.rootTarget || // return Document null ); @@ -772,9 +748,9 @@ export class EventService { return null; } - private pickTarget( + private async pickTarget( event: FederatedPointerEvent | FederatedWheelEvent, - ): INode { + ): Promise { return this.hitTest({ clientX: event.clientX, clientY: event.clientY, @@ -782,15 +758,15 @@ export class EventService { viewportY: event.viewportY, x: event.canvasX, y: event.canvasY, - }) as INode; + }) as Promise; } - private createPointerEvent( + private async createPointerEvent( from: FederatedPointerEvent, type?: string, target?: IEventTarget, fallbackTarget?: IEventTarget, - ): FederatedPointerEvent { + ): Promise { const event = this.allocateEvent(FederatedPointerEvent); this.copyPointerData(from, event); @@ -804,7 +780,8 @@ export class EventService { event.target = target ?? (existedHTML || - (this.isNativeEventFromCanvas(event) && this.pickTarget(event)) || + (this.isNativeEventFromCanvas(event) && + (await this.pickTarget(event))) || fallbackTarget); if (typeof type === 'string') { @@ -814,7 +791,9 @@ export class EventService { return event; } - private createWheelEvent(from: FederatedWheelEvent): FederatedWheelEvent { + private async createWheelEvent( + from: FederatedWheelEvent, + ): Promise { const event = this.allocateEvent(FederatedWheelEvent); this.copyWheelData(from, event); @@ -826,7 +805,7 @@ export class EventService { const existedHTML = this.getExistedHTML(event); event.target = existedHTML || - (this.isNativeEventFromCanvas(event) && this.pickTarget(event)); + (this.isNativeEventFromCanvas(event) && (await this.pickTarget(event))); return event; } diff --git a/packages/g-plugin-device-renderer/src/PickingPlugin.ts b/packages/g-plugin-device-renderer/src/PickingPlugin.ts index 6ed25ddaa..d1339de3d 100644 --- a/packages/g-plugin-device-renderer/src/PickingPlugin.ts +++ b/packages/g-plugin-device-renderer/src/PickingPlugin.ts @@ -83,18 +83,20 @@ export class PickingPlugin implements RenderingPlugin { */ renderingService.hooks.pickSync.tap( PickingPlugin.tag, - (result: PickingResult) => this.pick(result), + (result: PickingResult) => { + throw new Error('Sync version is not implemented.'); + }, ); renderingService.hooks.pick.tapPromise( PickingPlugin.tag, - async (result: PickingResult) => { + (result: PickingResult) => { return this.pick(result); }, ); } - private pick(result: PickingResult) { + private async pick(result: PickingResult) { const { topmost, position } = result; // use viewportX/Y @@ -119,7 +121,7 @@ export class PickingPlugin implements RenderingPlugin { // implements multi-layer picking // @see https://github.com/antvis/g/issues/948 - const pickedDisplayObjects = this.pickByRectangleInDepth( + const pickedDisplayObjects = await this.pickByRectangleInDepth( new Rectangle( clamp(Math.round(xInDevicePixel), 0, width - 1), clamp(Math.round(yInDevicePixel), 0, height - 1), @@ -133,16 +135,16 @@ export class PickingPlugin implements RenderingPlugin { return result; } - private pickByRectangleInDepth( + private async pickByRectangleInDepth( rect: Rectangle, depth = MAX_PICKING_DEPTH, - ): DisplayObject[] { + ): Promise { let picked = null; let counter = 1; const targets = []; do { - picked = this.pickByRectangle(rect, picked); + picked = await this.pickByRectangle(rect, picked); if (picked) { counter++; @@ -169,10 +171,10 @@ export class PickingPlugin implements RenderingPlugin { /** * return displayobjects in target rectangle */ - private pickByRectangle( + private async pickByRectangle( rect: Rectangle, picked: DisplayObject, - ): DisplayObject | null { + ): Promise { const device = this.renderGraphPlugin.getDevice(); const renderLists = this.renderGraphPlugin.getRenderLists(); @@ -213,161 +215,168 @@ export class PickingPlugin implements RenderingPlugin { // account for current view offset const currentView = { ...camera.getView() }; - // prevent unused RTs like main color being destroyed - this.renderHelper.renderGraph.renderTargetDeadPool.forEach((rt) => { - rt.age = -1; - }); + return new Promise((resolve) => { + // prevent unused RTs like main color being destroyed + this.renderHelper.renderGraph.renderTargetDeadPool.forEach((rt) => { + rt.age = -1; + }); - // picking pass - let target: DisplayObject; - builder.pushPass((pass) => { - pass.setDebugName('Picking Pass'); - pass.attachRenderTargetID(RGAttachmentSlot.Color0, pickingColorTargetID); - pass.attachRenderTargetID( - RGAttachmentSlot.DepthStencil, - mainDepthTargetID, - ); - pass.exec((passRenderer) => { - renderLists.picking.drawOnPassRenderer( - renderInstManager.renderCache, - passRenderer, + // picking pass + builder.pushPass((pass) => { + pass.setDebugName('Picking Pass'); + pass.attachRenderTargetID( + RGAttachmentSlot.Color0, + pickingColorTargetID, ); - }); - pass.post((scope) => { - const texture = scope.getRenderTargetTexture(RGAttachmentSlot.Color0); - - const readback = device.createReadback(); - - // restore previous view - if (currentView && currentView.enabled) { - camera.setViewOffset( - currentView.fullWidth, - currentView.fullHeight, - currentView.offsetX, - currentView.offsetY, - currentView.width, - currentView.height, + pass.attachRenderTargetID( + RGAttachmentSlot.DepthStencil, + mainDepthTargetID, + ); + pass.exec((passRenderer) => { + renderLists.picking.drawOnPassRenderer( + renderInstManager.renderCache, + passRenderer, ); - } else { - camera.clearViewOffset(); - } - - camera.setEnableUpdate(true); - - const pickedColors = readback.readTexture( - texture, - 0, - 0, - width, - height, - new Uint8Array(width * height * 4), - ) as Uint8Array; - - let pickedFeatureIdx = -1; - - if ( - pickedColors && - (pickedColors[0] !== 0 || - pickedColors[1] !== 0 || - pickedColors[2] !== 0) - ) { - pickedFeatureIdx = - this.pickingIdGenerator.decodePickingColor(pickedColors); - } - - if (pickedFeatureIdx > -1) { - const pickedDisplayObject = - this.pickingIdGenerator.getById(pickedFeatureIdx); - - if ( - pickedDisplayObject && - pickedDisplayObject.isVisible() && - pickedDisplayObject.isInteractive() - ) { - target = pickedDisplayObject; + }); + pass.post((scope) => { + const texture = scope.getRenderTargetTexture(RGAttachmentSlot.Color0); + + const readback = device.createReadback(); + + // restore previous view + if (currentView && currentView.enabled) { + camera.setViewOffset( + currentView.fullWidth, + currentView.fullHeight, + currentView.offsetX, + currentView.offsetY, + currentView.width, + currentView.height, + ); + } else { + camera.clearViewOffset(); } - } - readback.destroy(); + + camera.setEnableUpdate(true); + + readback + .readTexture( + texture, + 0, + 0, + width, + height, + new Uint8Array(width * height * 4), + ) + .then((pickedColors: Uint8Array) => { + let target: DisplayObject; + let pickedFeatureIdx = -1; + + if ( + pickedColors && + (pickedColors[0] !== 0 || + pickedColors[1] !== 0 || + pickedColors[2] !== 0) + ) { + pickedFeatureIdx = + this.pickingIdGenerator.decodePickingColor(pickedColors); + } + + if (pickedFeatureIdx > -1) { + const pickedDisplayObject = + this.pickingIdGenerator.getById(pickedFeatureIdx); + + if ( + pickedDisplayObject && + pickedDisplayObject.isVisible() && + pickedDisplayObject.isInteractive() + ) { + target = pickedDisplayObject; + } + } + readback.destroy(); + + resolve(target); + }); + }); }); - }); - // Push our outer template, which contains the dynamic UBO bindings... - const template = this.renderHelper.pushTemplateRenderInst(); - // SceneParams: binding = 0, ObjectParams: binding = 1 - template.setBindingLayouts([{ numUniformBuffers: 2, numSamplers: 0 }]); - template.setMegaStateFlags( - setAttachmentStateSimple( + // Push our outer template, which contains the dynamic UBO bindings... + const template = this.renderHelper.pushTemplateRenderInst(); + // SceneParams: binding = 0, ObjectParams: binding = 1 + template.setBindingLayouts([{ numUniformBuffers: 2, numSamplers: 0 }]); + template.setMegaStateFlags( + setAttachmentStateSimple( + { + depthWrite: true, + }, + { + rgbBlendMode: BlendMode.Add, + rgbBlendSrcFactor: BlendFactor.One, + rgbBlendDstFactor: BlendFactor.Zero, + alphaBlendMode: BlendMode.Add, + alphaBlendSrcFactor: BlendFactor.One, + alphaBlendDstFactor: BlendFactor.Zero, + }, + ), + ); + + // Update Scene Params + const { width: canvasWidth, height: canvasHeight } = this.context.config; + const dpr = this.context.contextService.getDPR(); + + camera.setEnableUpdate(false); + camera.setViewOffset( + canvasWidth * dpr, + canvasHeight * dpr, + x, + y, + width, + height, + ); + + template.setUniforms(SceneUniformBufferIndex, [ { - depthWrite: true, + name: SceneUniform.PROJECTION_MATRIX, + value: camera.getPerspective(), }, { - rgbBlendMode: BlendMode.Add, - rgbBlendSrcFactor: BlendFactor.One, - rgbBlendDstFactor: BlendFactor.Zero, - alphaBlendMode: BlendMode.Add, - alphaBlendSrcFactor: BlendFactor.One, - alphaBlendDstFactor: BlendFactor.Zero, + name: SceneUniform.VIEW_MATRIX, + value: camera.getViewTransform(), }, - ), - ); - - // Update Scene Params - const { width: canvasWidth, height: canvasHeight } = this.context.config; - const dpr = this.context.contextService.getDPR(); - - camera.setEnableUpdate(false); - camera.setViewOffset( - canvasWidth * dpr, - canvasHeight * dpr, - x, - y, - width, - height, - ); - - template.setUniforms(SceneUniformBufferIndex, [ - { - name: SceneUniform.PROJECTION_MATRIX, - value: camera.getPerspective(), - }, - { - name: SceneUniform.VIEW_MATRIX, - value: camera.getViewTransform(), - }, - { - name: SceneUniform.CAMERA_POSITION, - value: camera.getPosition(), - }, - { - name: SceneUniform.DEVICE_PIXEL_RATIO, - value: this.context.contextService.getDPR(), - }, - { - name: SceneUniform.VIEWPORT, - value: [canvasWidth, canvasHeight], - }, - { - name: SceneUniform.IS_ORTHO, - value: camera.isOrtho() ? 1 : 0, - }, - { - name: SceneUniform.IS_PICKING, - value: 1, - }, - ]); - - if (picked) { - this.batchManager.updateAttribute(picked, 'pointerEvents', false, true); - } - this.batchManager.render(renderLists.picking, true); + { + name: SceneUniform.CAMERA_POSITION, + value: camera.getPosition(), + }, + { + name: SceneUniform.DEVICE_PIXEL_RATIO, + value: this.context.contextService.getDPR(), + }, + { + name: SceneUniform.VIEWPORT, + value: [canvasWidth, canvasHeight], + }, + { + name: SceneUniform.IS_ORTHO, + value: camera.isOrtho() ? 1 : 0, + }, + { + name: SceneUniform.IS_PICKING, + value: 1, + }, + ]); - renderInstManager.popTemplateRenderInst(); + if (picked) { + this.batchManager.updateAttribute(picked, 'pointerEvents', false, true); + } + this.batchManager.render(renderLists.picking, true); - this.renderHelper.prepareToRender(); - this.renderHelper.renderGraph.execute(); + renderInstManager.popTemplateRenderInst(); - renderInstManager.resetRenderInsts(); + this.renderHelper.prepareToRender(); + this.renderHelper.renderGraph.execute(); - return target; + renderInstManager.resetRenderInsts(); + }); } } diff --git a/packages/g-plugin-device-renderer/src/platform/interfaces.ts b/packages/g-plugin-device-renderer/src/platform/interfaces.ts index 3ca52f1b4..bf9133fb4 100644 --- a/packages/g-plugin-device-renderer/src/platform/interfaces.ts +++ b/packages/g-plugin-device-renderer/src/platform/interfaces.ts @@ -79,7 +79,7 @@ export interface Readback extends ResourceBase { dst: ArrayBufferView, dstOffset?: number, length?: number, - ) => ArrayBufferView; + ) => Promise; readBuffer: ( b: Buffer, diff --git a/packages/g-plugin-webgl-device/src/platform/Readback.ts b/packages/g-plugin-webgl-device/src/platform/Readback.ts index cce958537..8c91a1e2c 100644 --- a/packages/g-plugin-webgl-device/src/platform/Readback.ts +++ b/packages/g-plugin-webgl-device/src/platform/Readback.ts @@ -71,7 +71,7 @@ export class Readback_GL extends ResourceBase_GL implements Readback { /** * @see https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/WebGL_best_practices#use_non-blocking_async_data_readback */ - readTexture( + async readTexture( t: Texture, x: number, y: number, @@ -80,7 +80,7 @@ export class Readback_GL extends ResourceBase_GL implements Readback { dstBuffer: ArrayBufferView, dstOffset = 0, length = dstBuffer.byteLength || 0, - ): ArrayBufferView { + ): Promise { const gl = this.device.gl; const texture = t as Texture_GL; diff --git a/packages/g-plugin-webgpu-device/src/platform/Readback.ts b/packages/g-plugin-webgpu-device/src/platform/Readback.ts index a5ac50534..d9e7e6d99 100644 --- a/packages/g-plugin-webgpu-device/src/platform/Readback.ts +++ b/packages/g-plugin-webgpu-device/src/platform/Readback.ts @@ -20,7 +20,7 @@ export class Readback_WebGPU extends ResourceBase_WebGPU implements Readback { super({ id, device }); } - readTexture( + async readTexture( t: Texture, x: number, y: number, @@ -29,7 +29,7 @@ export class Readback_WebGPU extends ResourceBase_WebGPU implements Readback { dst: ArrayBufferView, dstOffset = 0, length = 0, - ): ArrayBufferView { + ): Promise { const texture = t as Texture_WebGPU; const faceIndex = 0; @@ -75,9 +75,14 @@ export class Readback_WebGPU extends ResourceBase_WebGPU implements Readback { this.device.device.queue.submit([commandEncoder.finish()]); - // FIXME: read from buffer - // return this.readBuffer(buffer, size, dst, dstOffset, length, texture.pixelFormat); - return null; + return this.readBuffer( + buffer, + 0, + dst, + dstOffset, + length, + texture.pixelFormat, + ); } readBuffer( diff --git a/packages/g-plugin-webgpu-device/src/platform/interfaces.ts b/packages/g-plugin-webgpu-device/src/platform/interfaces.ts index e6a441af4..61c189f0e 100644 --- a/packages/g-plugin-webgpu-device/src/platform/interfaces.ts +++ b/packages/g-plugin-webgpu-device/src/platform/interfaces.ts @@ -9,7 +9,9 @@ import type { Device, BindingLayoutSamplerDescriptor, BindingLayoutDescriptor, + Buffer, } from '@antv/g-plugin-device-renderer'; +import { BufferDescriptor } from '@antv/g-plugin-device-renderer'; export interface TextureSharedDescriptor { dimension: TextureDimension; @@ -46,6 +48,7 @@ export interface IDevice_WebGPU extends Device { device: GPUDevice; getFallbackSampler: (samplerEntry: BindingLayoutSamplerDescriptor) => Sampler; getFallbackTexture: (samplerEntry: BindingLayoutSamplerDescriptor) => Texture; + createBuffer: (descriptor: BufferDescriptor) => Buffer; createTextureShared: ( descriptor: TextureSharedDescriptor, texture: TextureShared_WebGPU,