Skip to content

Commit 927bcc0

Browse files
authored
FrameGraph: add new task + several fixes (#17381)
In this PR: * Added a new `FrameGraphComputeShaderTask` (+ `NodeRenderGraphComputeShaderBlock`) class to simplify the management of compute shaders in frame graphs * Added a new `customIsReady` property in `FrameGraphExecuteTask` * Fixed directional and point shadows not working with frame graphs in some cases * Fixed `Mesh.ignoreCameraZ` not working with frame graphs * Regarding this one, making `Mesh.ignoreCameraMaxZ` work in `ObjectRenderer` may be difficult (it wasn't supported when the feature has been implemented a few years ago) and bad for performance, because it means the view/projection matrix changes during rendering, forcing us to update the scene ubo, which is bad in WebGPU. * Instead, I added a **ignoreCameraMaxZ** property in the `Camera` class. I feel it's the right place to put it, and there's no performance penalty to put it there. The catch is that all meshes rendered by the camera will ignore **maxZ** when the property is set. That shouldn't be a problem, though. What do you think? cc @sebavan, @deltakosh * Fixed crashes in NRGE in some cases ~~Don't review it yet, it may not be complete!~~ Ready for review!
1 parent b9269d0 commit 927bcc0

29 files changed

+486
-58
lines changed

packages/dev/core/src/Cameras/camera.ts

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,12 @@ export class Camera extends Node {
365365
@serialize()
366366
public isStereoscopicSideBySide: boolean;
367367

368+
/**
369+
* Ignores camera maxZ when computing the projection matrix (ie. use 0 instead of maxZ), meaning objects won't be culled by the far plane
370+
*/
371+
@serialize()
372+
public ignoreCameraMaxZ = false;
373+
368374
/**
369375
* Defines the list of custom render target which are rendered to and then used as the input to this camera's render. Eg. display another camera view on a TV in the main scene
370376
* This is pretty helpful if you wish to make a camera render to a texture you could reuse somewhere
@@ -653,7 +659,9 @@ export class Camera extends Node {
653659

654660
/** @internal */
655661
public _isSynchronizedProjectionMatrix(): boolean {
656-
let isSynchronized = this._cache.mode === this.mode && this._cache.minZ === this.minZ && this._cache.maxZ === this.maxZ;
662+
const maxZ = this.ignoreCameraMaxZ ? 0 : this.maxZ;
663+
664+
let isSynchronized = this._cache.mode === this.mode && this._cache.minZ === this.minZ && this._cache.maxZ === maxZ;
657665

658666
if (!isSynchronized) {
659667
return false;
@@ -933,10 +941,12 @@ export class Camera extends Node {
933941
return this._projectionMatrix;
934942
}
935943

944+
const maxZ = this.ignoreCameraMaxZ ? 0 : this.maxZ;
945+
936946
// Cache
937947
this._cache.mode = this.mode;
938948
this._cache.minZ = this.minZ;
939-
this._cache.maxZ = this.maxZ;
949+
this._cache.maxZ = maxZ;
940950

941951
// Matrix
942952
this._refreshFrustumPlanes = true;
@@ -974,8 +984,8 @@ export class Camera extends Node {
974984
getProjectionMatrix(
975985
this.fov,
976986
engine.getAspectRatio(this),
977-
reverseDepth ? this.maxZ : this.minZ,
978-
reverseDepth ? this.minZ : this.maxZ,
987+
reverseDepth ? maxZ : this.minZ,
988+
reverseDepth ? this.minZ : maxZ,
979989
this._projectionMatrix,
980990
this.fovMode === Camera.FOVMODE_VERTICAL_FIXED,
981991
engine.isNDCHalfZRange,
@@ -992,8 +1002,8 @@ export class Camera extends Node {
9921002
this.orthoRight ?? halfWidth,
9931003
this.orthoBottom ?? -halfHeight,
9941004
this.orthoTop ?? halfHeight,
995-
reverseDepth ? this.maxZ : this.minZ,
996-
reverseDepth ? this.minZ : this.maxZ,
1005+
reverseDepth ? maxZ : this.minZ,
1006+
reverseDepth ? this.minZ : maxZ,
9971007
this.oblique.length,
9981008
this.oblique.angle,
9991009
this._computeObliqueDistance(this.oblique.offset),
@@ -1006,8 +1016,8 @@ export class Camera extends Node {
10061016
this.orthoRight ?? halfWidth,
10071017
this.orthoBottom ?? -halfHeight,
10081018
this.orthoTop ?? halfHeight,
1009-
reverseDepth ? this.maxZ : this.minZ,
1010-
reverseDepth ? this.minZ : this.maxZ,
1019+
reverseDepth ? maxZ : this.minZ,
1020+
reverseDepth ? this.minZ : maxZ,
10111021
this._projectionMatrix,
10121022
engine.isNDCHalfZRange
10131023
);
@@ -1019,8 +1029,8 @@ export class Camera extends Node {
10191029
this.orthoRight ?? halfWidth,
10201030
this.orthoBottom ?? -halfHeight,
10211031
this.orthoTop ?? halfHeight,
1022-
reverseDepth ? this.maxZ : this.minZ,
1023-
reverseDepth ? this.minZ : this.maxZ,
1032+
reverseDepth ? maxZ : this.minZ,
1033+
reverseDepth ? this.minZ : maxZ,
10241034
this.oblique.length,
10251035
this.oblique.angle,
10261036
this._computeObliqueDistance(this.oblique.offset),
@@ -1033,8 +1043,8 @@ export class Camera extends Node {
10331043
this.orthoRight ?? halfWidth,
10341044
this.orthoBottom ?? -halfHeight,
10351045
this.orthoTop ?? halfHeight,
1036-
reverseDepth ? this.maxZ : this.minZ,
1037-
reverseDepth ? this.minZ : this.maxZ,
1046+
reverseDepth ? maxZ : this.minZ,
1047+
reverseDepth ? this.minZ : maxZ,
10381048
this._projectionMatrix,
10391049
engine.isNDCHalfZRange
10401050
);
@@ -1335,7 +1345,7 @@ export class Camera extends Node {
13351345
this._cameraRigParams.vrMetrics.aspectRatioFov,
13361346
this._cameraRigParams.vrMetrics.aspectRatio,
13371347
this.minZ,
1338-
this.maxZ,
1348+
this.ignoreCameraMaxZ ? 0 : this.maxZ,
13391349
this._cameraRigParams.vrWorkMatrix,
13401350
true,
13411351
this.getEngine().isNDCHalfZRange
@@ -1374,7 +1384,7 @@ export class Camera extends Node {
13741384
public _updateRigCameras() {
13751385
for (let i = 0; i < this._rigCameras.length; i++) {
13761386
this._rigCameras[i].minZ = this.minZ;
1377-
this._rigCameras[i].maxZ = this.maxZ;
1387+
this._rigCameras[i].maxZ = this.ignoreCameraMaxZ ? 0 : this.maxZ;
13781388
this._rigCameras[i].fov = this.fov;
13791389
this._rigCameras[i].upVector.copyFrom(this.upVector);
13801390
}

packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/bloomPostProcessBlock.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export class NodeRenderGraphBloomPostProcessBlock extends NodeRenderGraphBasePos
7272
}
7373

7474
/** The luminance threshold to find bright areas of the image to bloom. */
75-
@editableInPropertyPage("Threshold", PropertyTypeForEdition.Float, "PROPERTIES", { min: 0, max: 2 })
75+
@editableInPropertyPage("Threshold", PropertyTypeForEdition.Float, "PROPERTIES", { min: 0, max: 1 })
7676
public get threshold(): number {
7777
return this._frameGraphTask.bloom.threshold;
7878
}

packages/dev/core/src/FrameGraph/Node/Blocks/PostProcesses/chromaticAberrationPostProcessBlock.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export class NodeRenderGraphChromaticAberrationPostProcessBlock extends NodeRend
4444
}
4545

4646
/** The amount the effect will increase for pixels closer to the edge of the screen */
47-
@editableInPropertyPage("Radial intensity", PropertyTypeForEdition.Float, "PROPERTIES", { min: 0.1, max: 5 })
47+
@editableInPropertyPage("Radial intensity", PropertyTypeForEdition.Float, "PROPERTIES", { min: -1, max: 5 })
4848
public get radialIntensity(): number {
4949
return this._frameGraphTask.postProcess.radialIntensity;
5050
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import type { NodeRenderGraphConnectionPoint, Scene, FrameGraph, IComputeShaderPath, IComputeShaderOptions } from "core/index";
2+
import { RegisterClass } from "../../../Misc/typeStore";
3+
import { NodeRenderGraphBlockConnectionPointTypes } from "../Types/nodeRenderGraphTypes";
4+
import { NodeRenderGraphBlock } from "../nodeRenderGraphBlock";
5+
import { FrameGraphComputeShaderTask } from "../../Tasks/Misc/computeShaderTask";
6+
7+
/**
8+
* Block used to execute a compute shader in the frame graph
9+
*/
10+
export class NodeRenderGraphComputeShaderBlock extends NodeRenderGraphBlock {
11+
protected override _frameGraphTask: FrameGraphComputeShaderTask;
12+
13+
/**
14+
* Gets the frame graph task associated with this block
15+
*/
16+
public override get task() {
17+
return this._frameGraphTask;
18+
}
19+
20+
/**
21+
* Creates a new NodeRenderGraphComputeShaderBlock
22+
* @param name defines the block name
23+
* @param frameGraph defines the hosting frame graph
24+
* @param scene defines the hosting scene
25+
* @param computeShaderPath defines the compute shader path or source
26+
* @param computeShaderOptions defines the compute shader options
27+
*/
28+
public constructor(
29+
name: string,
30+
frameGraph: FrameGraph,
31+
scene: Scene,
32+
computeShaderPath: string | IComputeShaderPath = "@compute @workgroup_size(1, 1, 1)\nfn main() {}",
33+
computeShaderOptions: IComputeShaderOptions = { bindingsMapping: {} }
34+
) {
35+
super(name, frameGraph, scene);
36+
37+
this._additionalConstructionParameters = [computeShaderPath, computeShaderOptions];
38+
39+
this._addDependenciesInput(
40+
NodeRenderGraphBlockConnectionPointTypes.Camera | NodeRenderGraphBlockConnectionPointTypes.ShadowLight | NodeRenderGraphBlockConnectionPointTypes.ObjectList
41+
);
42+
43+
this.registerOutput("output", NodeRenderGraphBlockConnectionPointTypes.ResourceContainer);
44+
45+
this._frameGraphTask = new FrameGraphComputeShaderTask(name, frameGraph, computeShaderPath, computeShaderOptions);
46+
}
47+
48+
private _createTask(shaderPath: string | IComputeShaderPath, shaderOptions?: IComputeShaderOptions) {
49+
const dispatchSize = this._frameGraphTask.dispatchSize;
50+
const indirectDispatch = this._frameGraphTask.indirectDispatch;
51+
const execute = this._frameGraphTask.execute;
52+
53+
this._frameGraphTask.dispose();
54+
this._frameGraphTask = new FrameGraphComputeShaderTask(this.name, this._frameGraph, shaderPath, shaderOptions);
55+
56+
this._frameGraphTask.dispatchSize = dispatchSize;
57+
this._frameGraphTask.indirectDispatch = indirectDispatch;
58+
this._frameGraphTask.execute = execute;
59+
60+
this._additionalConstructionParameters = [shaderPath, shaderOptions];
61+
}
62+
63+
/**
64+
* Gets or sets the execute function
65+
*/
66+
public get shaderPath(): string | IComputeShaderPath {
67+
return this._frameGraphTask.computeShader.shaderPath;
68+
}
69+
70+
public set shaderPath(path: string | IComputeShaderPath) {
71+
this._createTask(path, this.shaderOptions);
72+
}
73+
74+
/**
75+
* Gets or sets the execute when task disabled function
76+
*/
77+
public get shaderOptions(): IComputeShaderOptions {
78+
return this._frameGraphTask.computeShader.options;
79+
}
80+
81+
public set shaderOptions(options: IComputeShaderOptions) {
82+
this._createTask(this.shaderPath, options);
83+
}
84+
85+
/**
86+
* Gets the current class name
87+
* @returns the class name
88+
*/
89+
public override getClassName() {
90+
return "NodeRenderGraphComputeShaderBlock";
91+
}
92+
93+
/**
94+
* Gets the output component
95+
*/
96+
public get output(): NodeRenderGraphConnectionPoint {
97+
return this._outputs[0];
98+
}
99+
}
100+
101+
RegisterClass("BABYLON.NodeRenderGraphComputeShaderBlock", NodeRenderGraphComputeShaderBlock);

packages/dev/core/src/FrameGraph/Node/Blocks/executeBlock.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { NodeRenderGraphConnectionPoint, Scene, FrameGraph } from "core/index";
1+
import type { NodeRenderGraphConnectionPoint, Scene, FrameGraph, FrameGraphContext } from "core/index";
22
import { RegisterClass } from "../../../Misc/typeStore";
33
import { NodeRenderGraphBlockConnectionPointTypes } from "../Types/nodeRenderGraphTypes";
44
import { NodeRenderGraphBlock } from "../nodeRenderGraphBlock";
@@ -35,6 +35,28 @@ export class NodeRenderGraphExecuteBlock extends NodeRenderGraphBlock {
3535
this._frameGraphTask = new FrameGraphExecuteTask(name, frameGraph);
3636
}
3737

38+
/**
39+
* Gets or sets the execute function
40+
*/
41+
public get func(): (context: FrameGraphContext) => void {
42+
return this._frameGraphTask.func;
43+
}
44+
45+
public set func(func: (context: FrameGraphContext) => void) {
46+
this._frameGraphTask.func = func;
47+
}
48+
49+
/**
50+
* Gets or sets the execute when task disabled function
51+
*/
52+
public get funcDisabled(): ((context: FrameGraphContext) => void) | undefined {
53+
return this._frameGraphTask.funcDisabled;
54+
}
55+
56+
public set funcDisabled(func: ((context: FrameGraphContext) => void) | undefined) {
57+
this._frameGraphTask.funcDisabled = func;
58+
}
59+
3860
/**
3961
* Gets the current class name
4062
* @returns the class name

packages/dev/core/src/FrameGraph/Node/Blocks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from "./computeShaderBlock";
12
export * from "./cullObjectsBlock";
23
export * from "./elbowBlock";
34
export * from "./executeBlock";

packages/dev/core/src/FrameGraph/Node/Blocks/inputBlock.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export class NodeRenderGraphInputBlock extends NodeRenderGraphBlock {
9090
formats: [Constants.TEXTUREFORMAT_RGBA],
9191
samples: 1,
9292
useSRGBBuffers: [false],
93+
creationFlags: [0],
9394
},
9495
sizeIsPercentage: true,
9596
isHistoryTexture: false,
@@ -105,6 +106,7 @@ export class NodeRenderGraphInputBlock extends NodeRenderGraphBlock {
105106
types: [Constants.TEXTURETYPE_UNSIGNED_BYTE],
106107
formats: [Constants.TEXTUREFORMAT_DEPTH24_STENCIL8],
107108
useSRGBBuffers: [false],
109+
creationFlags: [0],
108110
labels: [this.name],
109111
samples: 1,
110112
},

packages/dev/core/src/FrameGraph/Node/Blocks/outputBlock.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class NodeRenderGraphOutputBlock extends NodeRenderGraphBlock {
2929
this._isUnique = true;
3030

3131
this.registerInput("texture", NodeRenderGraphBlockConnectionPointTypes.Texture);
32+
this._addDependenciesInput();
3233

3334
this.texture.addAcceptedConnectionPointTypes(NodeRenderGraphBlockConnectionPointTypes.TextureAll);
3435

packages/dev/core/src/FrameGraph/Node/nodeRenderGraph.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,12 +366,16 @@ export class NodeRenderGraph {
366366
* Returns a promise that resolves when the node render graph is ready to be executed
367367
* This method must be called after the graph has been built (NodeRenderGraph.build called)!
368368
* @param timeStep Time step in ms between retries (default is 16)
369-
* @param maxTimeout Maximum time in ms to wait for the graph to be ready (default is 30000)
369+
* @param maxTimeout Maximum time in ms to wait for the graph to be ready (default is 5000)
370370
* @returns The promise that resolves when the graph is ready
371371
*/
372372
// eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
373-
public whenReadyAsync(timeStep = 16, maxTimeout = 30000): Promise<void> {
374-
return this._frameGraph.whenReadyAsync(timeStep, maxTimeout);
373+
public whenReadyAsync(timeStep = 16, maxTimeout = 5000): Promise<void> {
374+
this._frameGraph.pausedExecution = true;
375+
// eslint-disable-next-line github/no-then
376+
return this._frameGraph.whenReadyAsync(timeStep, maxTimeout).then(() => {
377+
this._frameGraph.pausedExecution = false;
378+
});
375379
}
376380

377381
/**

packages/dev/core/src/FrameGraph/Node/nodeRenderGraphBlock.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,14 @@ export class NodeRenderGraphBlock {
287287

288288
const dependencies = this.getInputByName("dependencies")!;
289289

290+
Object.defineProperty(this, "dependencies", {
291+
get: function (this: FrameGraphTask) {
292+
return dependencies;
293+
},
294+
enumerable: true,
295+
configurable: true,
296+
});
297+
290298
dependencies.addExcludedConnectionPointFromAllowedTypes(
291299
NodeRenderGraphBlockConnectionPointTypes.TextureAllButBackBuffer |
292300
NodeRenderGraphBlockConnectionPointTypes.ResourceContainer |

0 commit comments

Comments
 (0)