Skip to content

Commit c88976f

Browse files
authored
FrameGraph: adds support for frozen meshes + allows no output color texture for the geometry buffer renderer task (#17040)
This PR adds support for frozen meshes to frame graphs. Note that we have a bug when using multiple active cameras in a scene with meshes + instances. If we freeze the scene, only some instances (those that were culled and displayed by the last camera in the list) are displayed, due to the way we currently handle frozen meshes (we have a single `scene._activeMeshes` list, which is populated by a call to `_evaluateActiveMeshes` when `freezeActiveMeshes` is called). I'm not sure how to fix this bug in the current code, but the frame graph version of the same example works correctly because it uses object renderers to render meshes: * without frame graph: https://playground.babylonjs.com/#9SDNZF#30 * with frame graph: https://playground.babylonjs.com/#9SDNZF#29 The meshes are frozen after two seconds. In the first PG, some instances disappear along with the plane because they are displayed by the first camera. Note that you must wait for the PR to be merged to test the second PG, because otherwise the meshes will not be frozen even after calling `scene.freezeActiveMeshes()`! In addition, the PR allows you to not define any output color textures for the geometry buffer renderer. This allows you to generate only a depth render of the scene and reuse the depth buffer when rendering the scene later in the graph.
1 parent 0a4ed0c commit c88976f

File tree

24 files changed

+513
-107
lines changed

24 files changed

+513
-107
lines changed

packages/dev/core/src/Engines/Extensions/engine.multiRender.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ declare module "../../Engines/abstractEngine" {
6363
/**
6464
* Creates a layout object to draw/clear on specific textures in a MRT
6565
* @param textureStatus textureStatus[i] indicates if the i-th is active
66+
* @param backBufferLayout if true, the layout will be built to account for the back buffer only, and textureStatus won't be used
6667
* @returns A layout to be fed to the engine, calling `bindAttachments`.
6768
*/
68-
buildTextureLayout(textureStatus: boolean[]): number[];
69+
buildTextureLayout(textureStatus: boolean[], backBufferLayout?: boolean): number[];
6970

7071
/**
7172
* Restores the webgl state to only draw on the main color attachment
@@ -93,16 +94,20 @@ ThinEngine.prototype.restoreSingleAttachmentForRenderTarget = function (): void
9394
this.bindAttachments([gl.COLOR_ATTACHMENT0]);
9495
};
9596

96-
ThinEngine.prototype.buildTextureLayout = function (textureStatus: boolean[]): number[] {
97+
ThinEngine.prototype.buildTextureLayout = function (textureStatus: boolean[], backBufferLayout = false): number[] {
9798
const gl = this._gl;
9899

99100
const result = [];
100101

101-
for (let i = 0; i < textureStatus.length; i++) {
102-
if (textureStatus[i]) {
103-
result.push((<any>gl)["COLOR_ATTACHMENT" + i]);
104-
} else {
105-
result.push(gl.NONE);
102+
if (backBufferLayout) {
103+
result.push(gl.BACK);
104+
} else {
105+
for (let i = 0; i < textureStatus.length; i++) {
106+
if (textureStatus[i]) {
107+
result.push((<any>gl)["COLOR_ATTACHMENT" + i]);
108+
} else {
109+
result.push(gl.NONE);
110+
}
106111
}
107112
}
108113

packages/dev/core/src/Engines/WebGPU/Extensions/engine.multiRender.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ declare module "../../abstractEngine" {
6262
/**
6363
* Creates a layout object to draw/clear on specific textures in a MRT
6464
* @param textureStatus textureStatus[i] indicates if the i-th is active
65+
* @param backBufferLayout if true, the layout will be built to account for the back buffer only, and textureStatus won't be used
6566
* @returns A layout to be fed to the engine, calling `bindAttachments`.
6667
*/
67-
buildTextureLayout(textureStatus: boolean[]): number[];
68+
buildTextureLayout(textureStatus: boolean[], backBufferLayout?: boolean): number[];
6869

6970
/**
7071
* Restores the webgl state to only draw on the main color attachment
@@ -356,14 +357,18 @@ WebGPUEngine.prototype.bindAttachments = function (attachments: number[]): void
356357
}
357358
};
358359

359-
WebGPUEngine.prototype.buildTextureLayout = function (textureStatus: boolean[]): number[] {
360+
WebGPUEngine.prototype.buildTextureLayout = function (textureStatus: boolean[], backBufferLayout = false): number[] {
360361
const result = [];
361362

362-
for (let i = 0; i < textureStatus.length; i++) {
363-
if (textureStatus[i]) {
364-
result.push(i + 1);
365-
} else {
366-
result.push(0);
363+
if (backBufferLayout) {
364+
result.push(1);
365+
} else {
366+
for (let i = 0; i < textureStatus.length; i++) {
367+
if (textureStatus[i]) {
368+
result.push(i + 1);
369+
} else {
370+
result.push(0);
371+
}
367372
}
368373
}
369374

packages/dev/core/src/Engines/WebGPU/webgpuDrawContext.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ export class WebGPUDrawContext implements IDrawContext {
2525

2626
public uniqueId: number;
2727

28+
/**
29+
* @internal
30+
* By default, indirect draws are enabled in NON compatibility mode only
31+
* To enable indirect draws in compatibility mode (done by the end user), enableIndirectDraw must be set to true
32+
*/
33+
public _enableIndirectDrawInCompatMode = false;
34+
2835
/**
2936
* Buffers (uniform / storage) used for the draw call
3037
*/
@@ -64,6 +71,8 @@ export class WebGPUDrawContext implements IDrawContext {
6471
}
6572

6673
public set enableIndirectDraw(enable: boolean) {
74+
this._enableIndirectDrawInCompatMode = true;
75+
6776
if (this._enableIndirectDraw === enable) {
6877
return;
6978
}
@@ -99,7 +108,11 @@ export class WebGPUDrawContext implements IDrawContext {
99108
this._useInstancing = use;
100109
this._currentInstanceCount = -1;
101110

111+
const enableIndirectDrawInCompatMode = this._enableIndirectDrawInCompatMode;
112+
102113
this.enableIndirectDraw = use;
114+
115+
this._enableIndirectDrawInCompatMode = enableIndirectDrawInCompatMode;
103116
}
104117

105118
/**

packages/dev/core/src/Engines/webgpuEngine.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3775,7 +3775,7 @@ export class WebGPUEngine extends ThinWebGPUEngine {
37753775
// draw
37763776
const nonCompatMode = !this.compatibilityMode && !this._snapshotRendering.record;
37773777

3778-
if (this._currentDrawContext.indirectDrawBuffer) {
3778+
if ((nonCompatMode || this._currentDrawContext._enableIndirectDrawInCompatMode) && this._currentDrawContext.indirectDrawBuffer) {
37793779
this._currentDrawContext.setIndirectData(count, instancesCount || 1, start);
37803780
if (drawType === 0) {
37813781
renderPass2.drawIndexedIndirect(this._currentDrawContext.indirectDrawBuffer, 0);

packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/baseObjectRendererBlock.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,46 @@ export class NodeRenderGraphBaseObjectRendererBlock extends NodeRenderGraphBlock
100100
this._frameGraphTask.depthWrite = value;
101101
}
102102

103+
/** Indicates if particles should be rendered */
104+
@editableInPropertyPage("Render particles", PropertyTypeForEdition.Boolean, "PROPERTIES")
105+
public get renderParticles() {
106+
return this._frameGraphTask.renderParticles;
107+
}
108+
109+
public set renderParticles(value: boolean) {
110+
this._frameGraphTask.renderParticles = value;
111+
}
112+
113+
/** Indicates if sprites should be rendered */
114+
@editableInPropertyPage("Render sprites", PropertyTypeForEdition.Boolean, "PROPERTIES")
115+
public get renderSprites() {
116+
return this._frameGraphTask.renderSprites;
117+
}
118+
119+
public set renderSprites(value: boolean) {
120+
this._frameGraphTask.renderSprites = value;
121+
}
122+
123+
/** Indicates if layer mask check must be forced */
124+
@editableInPropertyPage("Force layer mask check", PropertyTypeForEdition.Boolean, "PROPERTIES")
125+
public get forceLayerMaskCheck() {
126+
return this._frameGraphTask.forceLayerMaskCheck;
127+
}
128+
129+
public set forceLayerMaskCheck(value: boolean) {
130+
this._frameGraphTask.forceLayerMaskCheck = value;
131+
}
132+
133+
/** Indicates if bounding boxes should be rendered */
134+
@editableInPropertyPage("Enable bounding box rendering", PropertyTypeForEdition.Boolean, "PROPERTIES")
135+
public get enableBoundingBoxRendering() {
136+
return this._frameGraphTask.enableBoundingBoxRendering;
137+
}
138+
139+
public set enableBoundingBoxRendering(value: boolean) {
140+
this._frameGraphTask.enableBoundingBoxRendering = value;
141+
}
142+
103143
/** Indicates if shadows must be enabled or disabled */
104144
@editableInPropertyPage("Disable shadows", PropertyTypeForEdition.Boolean, "PROPERTIES")
105145
public get disableShadows() {
@@ -224,6 +264,10 @@ export class NodeRenderGraphBaseObjectRendererBlock extends NodeRenderGraphBlock
224264
const codes: string[] = [];
225265
codes.push(`${this._codeVariableName}.depthTest = ${this.depthTest};`);
226266
codes.push(`${this._codeVariableName}.depthWrite = ${this.depthWrite};`);
267+
codes.push(`${this._codeVariableName}.renderParticles = ${this.renderParticles};`);
268+
codes.push(`${this._codeVariableName}.renderSprites = ${this.renderSprites};`);
269+
codes.push(`${this._codeVariableName}.forceLayerMaskCheck = ${this.forceLayerMaskCheck};`);
270+
codes.push(`${this._codeVariableName}.enableBoundingBoxRendering = ${this.enableBoundingBoxRendering};`);
227271
codes.push(`${this._codeVariableName}.disableShadows = ${this.disableShadows};`);
228272
codes.push(`${this._codeVariableName}.renderInLinearSpace = ${this.renderInLinearSpace};`);
229273
codes.push(`${this._codeVariableName}.isMainObjectRenderer = ${this.isMainObjectRenderer};`);
@@ -234,6 +278,10 @@ export class NodeRenderGraphBaseObjectRendererBlock extends NodeRenderGraphBlock
234278
const serializationObject = super.serialize();
235279
serializationObject.depthTest = this.depthTest;
236280
serializationObject.depthWrite = this.depthWrite;
281+
serializationObject.renderParticles = this.renderParticles;
282+
serializationObject.renderSprites = this.renderSprites;
283+
serializationObject.forceLayerMaskCheck = this.forceLayerMaskCheck;
284+
serializationObject.enableBoundingBoxRendering = this.enableBoundingBoxRendering;
237285
serializationObject.disableShadows = this.disableShadows;
238286
serializationObject.renderInLinearSpace = this.renderInLinearSpace;
239287
serializationObject.isMainObjectRenderer = this.isMainObjectRenderer;
@@ -244,6 +292,10 @@ export class NodeRenderGraphBaseObjectRendererBlock extends NodeRenderGraphBlock
244292
super._deserialize(serializationObject);
245293
this.depthTest = serializationObject.depthTest;
246294
this.depthWrite = serializationObject.depthWrite;
295+
this.renderParticles = serializationObject.renderParticles ?? true;
296+
this.renderSprites = serializationObject.renderSprites ?? true;
297+
this.forceLayerMaskCheck = serializationObject.forceLayerMaskCheck ?? true;
298+
this.enableBoundingBoxRendering = serializationObject.enableBoundingBoxRendering ?? true;
247299
this.disableShadows = serializationObject.disableShadows;
248300
this.renderInLinearSpace = !!serializationObject.renderInLinearSpace;
249301
this.isMainObjectRenderer = !!serializationObject.isMainObjectRenderer;

packages/dev/core/src/FrameGraph/Node/Blocks/Rendering/geometryRendererBlock.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -377,10 +377,6 @@ export class NodeRenderGraphGeometryRendererBlock extends NodeRenderGraphBlock {
377377
this.geomLinearVelocity.isConnected,
378378
];
379379

380-
if (textureActivation.every((t) => !t)) {
381-
throw new Error("NodeRenderGraphGeometryRendererBlock: At least one output geometry buffer must be connected");
382-
}
383-
384380
this.outputDepth.value = this._frameGraphTask.outputDepthTexture;
385381
this.geomViewDepth.value = this._frameGraphTask.geometryViewDepthTexture;
386382
this.geomNormViewDepth.value = this._frameGraphTask.geometryNormViewDepthTexture;

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { NodeRenderGraphBlockConnectionPointTypes } from "./Types/nodeRenderGrap
2626
import { NodeRenderGraphClearBlock } from "./Blocks/Textures/clearBlock";
2727
import { NodeRenderGraphObjectRendererBlock } from "./Blocks/Rendering/objectRendererBlock";
2828
import { NodeRenderGraphBuildState } from "./nodeRenderGraphBuildState";
29+
import { NodeRenderGraphCullObjectsBlock } from "./Blocks/cullObjectsBlock";
2930

3031
// declare NODERENDERGRAPHEDITOR namespace for compilation issue
3132
// eslint-disable-next-line @typescript-eslint/naming-convention
@@ -625,14 +626,19 @@ export class NodeRenderGraph {
625626
colorTexture.output.connectTo(clear.target);
626627
depthTexture.output.connectTo(clear.depth);
627628

628-
// Render objects
629+
// Object list and culling
629630
const camera = new NodeRenderGraphInputBlock("Camera", this._frameGraph, this._scene, NodeRenderGraphBlockConnectionPointTypes.Camera);
630631
const objectList = new NodeRenderGraphInputBlock("Object List", this._frameGraph, this._scene, NodeRenderGraphBlockConnectionPointTypes.ObjectList);
632+
const cull = new NodeRenderGraphCullObjectsBlock("Cull", this._frameGraph, this._scene);
633+
634+
camera.output.connectTo(cull.camera);
635+
objectList.output.connectTo(cull.objects);
631636

637+
// Render objects
632638
const mainRendering = new NodeRenderGraphObjectRendererBlock("Main Rendering", this._frameGraph, this._scene);
633639

634640
camera.output.connectTo(mainRendering.camera);
635-
objectList.output.connectTo(mainRendering.objects);
641+
cull.output.connectTo(mainRendering.objects);
636642
clear.output.connectTo(mainRendering.target);
637643
clear.outputDepth.connectTo(mainRendering.depth);
638644

packages/dev/core/src/FrameGraph/Tasks/Layers/baseLayerTask.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ export class FrameGraphBaseLayerTask extends FrameGraphTask {
371371
if (
372372
!this.layer.shouldRender() ||
373373
info.renderingGroupId !== this.layer._options.renderingGroupId ||
374-
info.renderingManager !== this.objectRendererTask.objectRenderer._renderingManager
374+
info.renderingManager !== this.objectRendererTask.objectRenderer.renderingManager
375375
) {
376376
return;
377377
}

packages/dev/core/src/FrameGraph/Tasks/Misc/cullObjectsTask.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ export class FrameGraphCullObjectsTask extends FrameGraphTask {
3232
super(name, frameGraph);
3333
this._scene = scene;
3434
this.outputObjectList = {
35-
meshes: [],
36-
particleSystems: [],
35+
meshes: null,
36+
particleSystems: null,
3737
};
3838
}
3939

@@ -42,10 +42,22 @@ export class FrameGraphCullObjectsTask extends FrameGraphTask {
4242
throw new Error(`FrameGraphCullObjectsTask ${this.name}: objectList and camera are required`);
4343
}
4444

45+
// Initial output values
46+
this.outputObjectList.meshes = this.objectList.meshes;
47+
this.outputObjectList.particleSystems = this.objectList.particleSystems;
48+
4549
const pass = this._frameGraph.addCullPass(this.name);
4650

4751
pass.setObjectList(this.outputObjectList);
4852
pass.setExecuteFunc((_context) => {
53+
// No culling on particle systems
54+
this.outputObjectList.particleSystems = this.objectList.particleSystems;
55+
56+
if (this._scene._activeMeshesFrozen) {
57+
// If active meshes are frozen, we don't need culling: we keep the last list created before freezing
58+
return;
59+
}
60+
4961
this.outputObjectList.meshes = [];
5062

5163
this.camera._updateFrustumPlanes();

packages/dev/core/src/FrameGraph/Tasks/Rendering/geometryRendererTask.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,8 @@ export class FrameGraphGeometryRendererTask extends FrameGraphTask {
300300
}
301301

302302
public record() {
303-
if (this.textureDescriptions.length === 0 || this.objectList === undefined) {
304-
throw new Error(`FrameGraphGeometryRendererTask ${this.name}: object list and at least one geometry texture description must be provided`);
303+
if (this.objectList === undefined) {
304+
throw new Error(`FrameGraphGeometryRendererTask ${this.name}: object list must be provided`);
305305
}
306306

307307
// Make sure the renderList / particleSystemList are set when FrameGraphGeometryRendererTask.isReady() is called!
@@ -316,10 +316,10 @@ export class FrameGraphGeometryRendererTask extends FrameGraphTask {
316316

317317
this._registerForRenderPassId(this._renderer.renderPassId);
318318

319-
const outputTextureDescription = this._frameGraph.textureManager.getTextureDescription(outputTextureHandle[0]);
319+
const outputTextureDescription = outputTextureHandle.length > 0 ? this._frameGraph.textureManager.getTextureDescription(outputTextureHandle[0]) : null;
320320

321-
this._textureWidth = outputTextureDescription.size.width;
322-
this._textureHeight = outputTextureDescription.size.height;
321+
this._textureWidth = outputTextureDescription?.size.width ?? 0;
322+
this._textureHeight = outputTextureDescription?.size.height ?? 0;
323323

324324
// Create pass
325325
MaterialHelperGeometryRendering.MarkAsDirty(this._renderer.renderPassId, this.objectList.meshes || this._scene.meshes);
@@ -457,7 +457,7 @@ export class FrameGraphGeometryRendererTask extends FrameGraphTask {
457457
}
458458

459459
const depthTextureDescription = this._frameGraph.textureManager.getTextureDescription(this.depthTexture);
460-
if (depthTextureDescription.options.samples !== this.samples) {
460+
if (depthTextureDescription.options.samples !== this.samples && this.textureDescriptions.length > 0) {
461461
throw new Error(`FrameGraphGeometryRendererTask ${this.name}: the depth texture and the output texture must have the same number of samples`);
462462
}
463463

0 commit comments

Comments
 (0)