Skip to content

Commit

Permalink
fix: WebGPU HAL & handle lost context #863 & #1362
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoiver committed Jun 13, 2023
1 parent 037f76e commit bc371cf
Show file tree
Hide file tree
Showing 46 changed files with 1,032 additions and 569 deletions.
70 changes: 34 additions & 36 deletions packages/g-lite/src/Canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,57 +510,55 @@ export class Canvas extends EventTarget implements ICanvas {

if (this.context.contextService.init) {
this.context.contextService.init();
this.initRenderingService(renderer, firstContentfullPaint);
if (firstContentfullPaint) {
this.requestAnimationFrame(() => {
this.dispatchEvent(new CustomEvent(CanvasEvent.READY));
});
if (this.readyPromise) {
this.resolveReadyPromise();
}
} else {
this.dispatchEvent(new CustomEvent(CanvasEvent.RENDERER_CHANGED));
}
this.initRenderingService(renderer, firstContentfullPaint, true);
} else {
this.context.contextService.initAsync().then(() => {
this.initRenderingService(renderer, firstContentfullPaint);
if (firstContentfullPaint) {
this.dispatchEvent(new CustomEvent(CanvasEvent.READY));
if (this.readyPromise) {
this.resolveReadyPromise();
}
} else {
this.dispatchEvent(new CustomEvent(CanvasEvent.RENDERER_CHANGED));
}
});
}
}

private initRenderingService(
renderer: IRenderer,
firstContentfullPaint = false,
async = false,
) {
this.context.renderingService.init();

this.inited = true;
this.context.renderingService.init(() => {
this.inited = true;

if (!firstContentfullPaint) {
this.getRoot().forEach((node) => {
const renderable = (node as Element).renderable;
if (renderable) {
renderable.renderBoundsDirty = true;
renderable.boundsDirty = true;
renderable.dirty = true;
if (firstContentfullPaint) {
if (async) {
this.requestAnimationFrame(() => {
this.dispatchEvent(new CustomEvent(CanvasEvent.READY));
});
} else {
this.dispatchEvent(new CustomEvent(CanvasEvent.READY));
}
});
}
if (this.readyPromise) {
this.resolveReadyPromise();
}
} else {
this.dispatchEvent(new CustomEvent(CanvasEvent.RENDERER_CHANGED));
}

if (!firstContentfullPaint) {
this.getRoot().forEach((node) => {
const renderable = (node as Element).renderable;
if (renderable) {
renderable.renderBoundsDirty = true;
renderable.boundsDirty = true;
renderable.dirty = true;
}
});
}

// keep current scenegraph unchanged, just trigger mounted event
this.mountChildren(this.getRoot());
// keep current scenegraph unchanged, just trigger mounted event
this.mountChildren(this.getRoot());

if (renderer.getConfig().enableAutoRendering) {
this.run();
}
if (renderer.getConfig().enableAutoRendering) {
this.run();
}
});
}

private loadRendererContainerModule(renderer: IRenderer) {
Expand Down
15 changes: 13 additions & 2 deletions packages/g-lite/src/services/RenderingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
CanvasConfig,
} from '../types';
import {
AsyncParallelHook,
AsyncSeriesWaterfallHook,
sortByZIndex,
sortedIndex,
Expand Down Expand Up @@ -72,6 +73,7 @@ export class RenderingService {
* called before any frame rendered
*/
init: new SyncHook<[]>(),
initAsync: new AsyncParallelHook<[]>(),
/**
* only dirty object which has sth changed will be rendered
*/
Expand Down Expand Up @@ -120,15 +122,24 @@ export class RenderingService {
click: new SyncHook<[InteractivePointerEvent]>(),
};

init() {
init(callback: () => void) {
const context = { ...this.globalRuntime, ...this.context };

// register rendering plugins
this.context.renderingPlugins.forEach((plugin) => {
plugin.apply(context, runtime);
});
this.hooks.init.call();
this.inited = true;

if (this.hooks.initAsync.getCallbacksNum() === 0) {
this.inited = true;
callback();
} else {
this.hooks.initAsync.promise().then(() => {
this.inited = true;
callback();
});
}
}

getStats() {
Expand Down
4 changes: 4 additions & 0 deletions packages/g-lite/src/utils/tapable/AsyncParallelHook.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export class AsyncParallelHook<T> {
private callbacks: ((...args: T[]) => Promise<void>)[] = [];

getCallbacksNum() {
return this.callbacks.length;
}

tapPromise(options: string, fn: (...args: T[]) => Promise<void>) {
this.callbacks.push(fn);
}
Expand Down
32 changes: 8 additions & 24 deletions packages/g-plugin-box2d/src/Box2DPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export class Box2DPlugin implements RenderingPlugin {

private bodies: Map<DisplayObject, Box2DBody> = new Map();
private fixtures: WeakMap<Box2D.b2Fixture, DisplayObject> = new WeakMap();
private pendingDisplayObjects: DisplayObject[] = [];

apply(context: RenderingPluginContext) {
const { renderingService, renderingContext } = context;
Expand All @@ -74,13 +73,8 @@ export class Box2DPlugin implements RenderingPlugin {
};

const handleMounted = (e: FederatedEvent) => {
const Box2D = this.Box2D;
const target = e.target as DisplayObject;
if (Box2D) {
this.addActor(target);
} else {
this.pendingDisplayObjects.push(target);
}
this.addActor(target);
};

const handleUnmounted = (e: FederatedEvent) => {
Expand Down Expand Up @@ -155,25 +149,22 @@ export class Box2DPlugin implements RenderingPlugin {
}
};

renderingService.hooks.init.tap(Box2DPlugin.tag, () => {
renderingService.hooks.initAsync.tapPromise(Box2DPlugin.tag, async () => {
canvas.addEventListener(ElementEvent.MOUNTED, handleMounted);
canvas.addEventListener(ElementEvent.UNMOUNTED, handleUnmounted);
canvas.addEventListener(
ElementEvent.ATTR_MODIFIED,
handleAttributeChanged,
);

(async () => {
this.Box2D = await this.loadBox2D();
this.Box2D = await this.loadBox2D();

this.temp = new this.Box2D.b2Vec2(0, 0);
this.temp2 = new this.Box2D.b2Vec2(0, 0);
this.createScene();
this.handlePendingDisplayObjects();
this.temp = new this.Box2D.b2Vec2(0, 0);
this.temp2 = new this.Box2D.b2Vec2(0, 0);
this.createScene();

// do simulation each frame
canvas.addEventListener(CanvasEvent.BEFORE_RENDER, simulate);
})();
// do simulation each frame
canvas.addEventListener(CanvasEvent.BEFORE_RENDER, simulate);
});

renderingService.hooks.destroy.tap(Box2DPlugin.tag, () => {
Expand Down Expand Up @@ -407,13 +398,6 @@ export class Box2DPlugin implements RenderingPlugin {
}
}

private handlePendingDisplayObjects() {
this.pendingDisplayObjects.forEach((object) => {
this.addActor(object);
});
this.pendingDisplayObjects = [];
}

private async loadBox2D(): Promise<typeof Box2D & EmscriptenModule> {
const hasSIMD = WebAssembly.validate(
new Uint8Array([
Expand Down
65 changes: 22 additions & 43 deletions packages/g-plugin-device-renderer/src/RenderGraphPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ export class RenderGraphPlugin implements RenderingPlugin {
*/
private builder: RGGraphBuilder;

private pendingDisplayObjects: DisplayObject[] = [];

private enableCapture: boolean;
private captureOptions: Partial<DataURLOptions>;
private capturePromise: Promise<any> | undefined;
Expand Down Expand Up @@ -122,11 +120,7 @@ export class RenderGraphPlugin implements RenderingPlugin {
// @ts-ignore
object.renderable3D = renderable3D;

if (this.swapChain) {
this.batchManager.add(object);
} else {
this.pendingDisplayObjects.push(object);
}
this.batchManager.add(object);
};

const handleUnmounted = (e: FederatedEvent) => {
Expand Down Expand Up @@ -177,24 +171,28 @@ export class RenderGraphPlugin implements RenderingPlugin {
}
};

renderingService.hooks.init.tap(RenderGraphPlugin.tag, () => {
canvas.addEventListener(ElementEvent.MOUNTED, handleMounted);
canvas.addEventListener(ElementEvent.UNMOUNTED, handleUnmounted);
canvas.addEventListener(
ElementEvent.ATTR_MODIFIED,
handleAttributeChanged,
);
canvas.addEventListener(ElementEvent.BOUNDS_CHANGED, handleBoundsChanged);
this.context.config.renderer.getConfig().enableDirtyRectangleRendering =
false;
renderingService.hooks.initAsync.tapPromise(
RenderGraphPlugin.tag,
async () => {
canvas.addEventListener(ElementEvent.MOUNTED, handleMounted);
canvas.addEventListener(ElementEvent.UNMOUNTED, handleUnmounted);
canvas.addEventListener(
ElementEvent.ATTR_MODIFIED,
handleAttributeChanged,
);
canvas.addEventListener(
ElementEvent.BOUNDS_CHANGED,
handleBoundsChanged,
);
this.context.config.renderer.getConfig().enableDirtyRectangleRendering =
false;

const $canvas =
this.context.contextService.getDomElement() as HTMLCanvasElement;
const $canvas =
this.context.contextService.getDomElement() as HTMLCanvasElement;

const { width, height } = this.context.config;
this.context.contextService.resize(width, height);
const { width, height } = this.context.config;
this.context.contextService.resize(width, height);

(async () => {
// create swap chain and get device
// @ts-ignore
this.swapChain = await this.context.deviceContribution.createSwapChain(
Expand All @@ -213,16 +211,8 @@ export class RenderGraphPlugin implements RenderingPlugin {
device: this.device,
...context,
});

this.pendingDisplayObjects.forEach((object) => {
this.batchManager.add(object);
});
this.pendingDisplayObjects = [];

// trigger rerender
this.context.renderingService.dirtify();
})();
});
},
);

renderingService.hooks.destroy.tap(RenderGraphPlugin.tag, () => {
this.renderHelper.destroy();
Expand All @@ -243,10 +233,6 @@ export class RenderGraphPlugin implements RenderingPlugin {
* build frame graph at the beginning of each frame
*/
renderingService.hooks.beginFrame.tap(RenderGraphPlugin.tag, () => {
if (!this.swapChain) {
return;
}

const canvas = this.swapChain.getCanvas() as HTMLCanvasElement;
const renderInstManager = this.renderHelper.renderInstManager;
this.builder = this.renderHelper.renderGraph.newGraphBuilder();
Expand Down Expand Up @@ -321,10 +307,6 @@ export class RenderGraphPlugin implements RenderingPlugin {
});

renderingService.hooks.endFrame.tap(RenderGraphPlugin.tag, () => {
if (!this.swapChain) {
return;
}

const renderInstManager = this.renderHelper.renderInstManager;

// TODO: time for GPU Animation
Expand Down Expand Up @@ -394,9 +376,6 @@ export class RenderGraphPlugin implements RenderingPlugin {

renderInstManager.resetRenderInsts();

// output to screen
this.swapChain.present();

// capture here since we don't preserve drawing buffer
if (this.enableCapture && this.resolveCapturePromise) {
const { type, encoderOptions } = this.captureOptions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export class BufferGeometry<GeometryProps = any> extends EventEmitter {

this.indexBuffer = makeStaticDataBuffer(
this.device,
BufferUsage.INDEX | BufferUsage.COPY_DST,
BufferUsage.INDEX,
new Uint32Array(ArrayBuffer.isView(indices) ? indices.buffer : indices)
.buffer,
);
Expand Down Expand Up @@ -180,7 +180,7 @@ export class BufferGeometry<GeometryProps = any> extends EventEmitter {

const buffer = makeStaticDataBuffer(
this.device,
BufferUsage.VERTEX | BufferUsage.COPY_DST,
BufferUsage.VERTEX,
data.buffer,
);
this.vertexBuffers[bufferIndex] = buffer;
Expand Down Expand Up @@ -222,7 +222,9 @@ export class BufferGeometry<GeometryProps = any> extends EventEmitter {
if (this.indexBuffer) {
this.indexBuffer.setSubData(
offset,
ArrayBuffer.isView(indices) ? indices : new Uint32Array(indices),
new Uint8Array(
ArrayBuffer.isView(indices) ? indices : new Uint32Array(indices),
),
);
}
return this;
Expand Down
Loading

0 comments on commit bc371cf

Please sign in to comment.