Skip to content

Commit 3dc3559

Browse files
authored
WebGPU: fix fast snapshot rendering mode + add support for it to frame graphs (#17064)
1 parent 7f5255c commit 3dc3559

File tree

9 files changed

+89
-27
lines changed

9 files changed

+89
-27
lines changed

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,8 @@ declare module "../../scene" {
183183
}
184184
}
185185

186-
function CreateMultiviewUbo(engine: AbstractEngine, name?: string) {
187-
const ubo = new UniformBuffer(engine, undefined, true, name);
186+
function CreateMultiviewUbo(engine: AbstractEngine, name?: string, trackUBOsInFrame?: boolean) {
187+
const ubo = new UniformBuffer(engine, undefined, true, name, undefined, trackUBOsInFrame);
188188
ubo.addUniform("viewProjection", 16);
189189
ubo.addUniform("viewProjectionR", 16);
190190
ubo.addUniform("view", 16);
@@ -200,11 +200,11 @@ Scene.prototype._multiviewSceneUbo = null;
200200
Scene.prototype._createMultiviewUbo = function () {
201201
this._multiviewSceneUbo = CreateMultiviewUbo(this.getEngine(), "scene_multiview");
202202
};
203-
Scene.prototype.createSceneUniformBuffer = function (name?: string): UniformBuffer {
203+
Scene.prototype.createSceneUniformBuffer = function (name?: string, trackUBOsInFrame?: boolean): UniformBuffer {
204204
if (this._multiviewSceneUbo) {
205-
return CreateMultiviewUbo(this.getEngine(), name);
205+
return CreateMultiviewUbo(this.getEngine(), name, trackUBOsInFrame);
206206
}
207-
return CurrentCreateSceneUniformBuffer.bind(this)(name);
207+
return CurrentCreateSceneUniformBuffer.bind(this)(name, trackUBOsInFrame);
208208
};
209209
Scene.prototype._updateMultiviewUbo = function (viewR?: Matrix, projectionR?: Matrix) {
210210
if (viewR && projectionR) {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3444,6 +3444,11 @@ export class WebGPUEngine extends ThinWebGPUEngine {
34443444
}
34453445

34463446
// We don't create the render pass just now, we do a lazy creation of the render pass, hoping the render pass will be created by a call to clear()...
3447+
// However, if snapshot rendering is enabled, we need to create the render pass immediately, to be sure currentRenderPass is not null when _endCurrentRenderPass() is called.
3448+
// (as in snapshot rendering mode, we may not have a call to clear() before _endCurrentRenderPass(), so lazy creation would not work)
3449+
if (this._snapshotRendering.play || this._snapshotRendering.record) {
3450+
this._startRenderTargetRenderPass(this._currentRenderTarget, false, null, false, false);
3451+
}
34473452

34483453
if (this._cachedViewport && !forceFullscreenViewport) {
34493454
this.setViewport(this._cachedViewport, requiredWidth, requiredHeight);

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,13 @@ export class FrameGraphBaseLayerTask extends FrameGraphTask {
109109
}
110110
}
111111

112+
/**
113+
* Gets the object renderer used to render the layer.
114+
*/
115+
public get objectRendererForLayer() {
116+
return this._objectRendererForLayer;
117+
}
118+
112119
protected readonly _scene: Scene;
113120
protected readonly _engine: AbstractEngine;
114121
protected readonly _clearLayerTextures: FrameGraphClearTextureTask;

packages/dev/core/src/Lights/Shadows/cascadedShadowGenerator.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,7 @@ export class CascadedShadowGenerator extends ShadowGenerator {
804804

805805
protected override _createTargetRenderTexture(): void {
806806
const engine = this._scene.getEngine();
807+
this._shadowMap?.dispose();
807808
const size = { width: this._mapSize, height: this._mapSize, layers: this.numCascades };
808809
this._shadowMap = new RenderTargetTexture(
809810
this._light.name + "_CSMShadowMap",

packages/dev/core/src/Lights/Shadows/shadowGenerator.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,7 @@ export class ShadowGenerator implements IShadowGenerator {
951951

952952
protected _createTargetRenderTexture(): void {
953953
const engine = this._scene.getEngine();
954+
this._shadowMap?.dispose();
954955
if (engine._features.supportDepthStencilTexture) {
955956
this._shadowMap = new RenderTargetTexture(
956957
this._light.name + "_shadowMap",

packages/dev/core/src/Materials/uniformBuffer.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export class UniformBuffer {
4040
private _currentEffectName: string;
4141
private _name: string;
4242
private _currentFrameId: number;
43+
private _trackUBOsInFrame: boolean;
4344

4445
// Pool for avoiding memory leaks
4546
private static _MAX_UNIFORM_SIZE = 256;
@@ -236,8 +237,9 @@ export class UniformBuffer {
236237
* @param dynamic Define if the buffer is updatable
237238
* @param name to assign to the buffer (debugging purpose)
238239
* @param forceNoUniformBuffer define that this object must not rely on UBO objects
240+
* @param trackUBOsInFrame define if the UBOs should be tracked in the frame (default: undefined - will use the value from Engine._features.trackUbosInFrame)
239241
*/
240-
constructor(engine: AbstractEngine, data?: number[], dynamic = false, name?: string, forceNoUniformBuffer = false) {
242+
constructor(engine: AbstractEngine, data?: number[], dynamic = false, name?: string, forceNoUniformBuffer = false, trackUBOsInFrame?: boolean) {
241243
this._engine = engine;
242244
this._noUBO = !engine.supportsUniformBuffers || forceNoUniformBuffer;
243245
this._dynamic = dynamic;
@@ -250,12 +252,14 @@ export class UniformBuffer {
250252
this._uniformArraySizes = {};
251253
this._uniformLocationPointer = 0;
252254
this._needSync = false;
255+
this._trackUBOsInFrame = false;
253256

254-
if (this._engine._features.trackUbosInFrame) {
257+
if ((trackUBOsInFrame === undefined && this._engine._features.trackUbosInFrame) || trackUBOsInFrame === true) {
255258
this._buffers = [];
256259
this._bufferIndex = -1;
257260
this._createBufferOnWrite = false;
258261
this._currentFrameId = 0;
262+
this._trackUBOsInFrame = true;
259263
}
260264

261265
if (this._noUBO) {
@@ -587,7 +591,7 @@ export class UniformBuffer {
587591
this._buffer = this._engine.createUniformBuffer(this._bufferData, this._name + "_UniformList:" + this._getNames());
588592
}
589593

590-
if (this._engine._features.trackUbosInFrame) {
594+
if (this._trackUBOsInFrame) {
591595
this._buffers.push([this._buffer, this._engine._features.checkUbosContentBeforeUpload ? this._bufferData.slice() : undefined]);
592596
this._bufferIndex = this._buffers.length - 1;
593597
this._createBufferOnWrite = false;
@@ -596,7 +600,7 @@ export class UniformBuffer {
596600

597601
/** @internal */
598602
public _rebuildAfterContextLost(): void {
599-
if (this._engine._features.trackUbosInFrame) {
603+
if (this._trackUBOsInFrame) {
600604
this._buffers = [];
601605
this._currentFrameId = 0;
602606
}
@@ -613,11 +617,15 @@ export class UniformBuffer {
613617
return this._bufferIndex;
614618
}
615619

616-
/** Gets the name of this buffer */
620+
/** Gets or sets the name of this buffer */
617621
public get name(): string {
618622
return this._name;
619623
}
620624

625+
public set name(value: string) {
626+
this._name = value;
627+
}
628+
621629
/** Gets the current effect */
622630
public get currentEffect(): Nullable<Effect> {
623631
return this._currentEffect;
@@ -656,14 +664,14 @@ export class UniformBuffer {
656664
}
657665

658666
if (!this._dynamic && !this._needSync) {
659-
this._createBufferOnWrite = this._engine._features.trackUbosInFrame;
667+
this._createBufferOnWrite = this._trackUBOsInFrame;
660668
return;
661669
}
662670

663671
if (this._buffers && this._buffers.length > 1 && this._buffers[this._bufferIndex][1]) {
664672
if (this._buffersEqual(this._bufferData, this._buffers[this._bufferIndex][1]!)) {
665673
this._needSync = false;
666-
this._createBufferOnWrite = this._engine._features.trackUbosInFrame;
674+
this._createBufferOnWrite = this._trackUBOsInFrame;
667675
return;
668676
} else {
669677
this._copyBuffer(this._bufferData, this._buffers[this._bufferIndex][1]!);
@@ -680,7 +688,7 @@ export class UniformBuffer {
680688
}
681689

682690
this._needSync = false;
683-
this._createBufferOnWrite = this._engine._features.trackUbosInFrame;
691+
this._createBufferOnWrite = this._trackUBOsInFrame;
684692
}
685693

686694
private _createNewBuffer() {
@@ -695,7 +703,7 @@ export class UniformBuffer {
695703
}
696704

697705
private _checkNewFrame(): void {
698-
if (this._engine._features.trackUbosInFrame && this._currentFrameId !== this._engine.frameId) {
706+
if (this._trackUBOsInFrame && this._currentFrameId !== this._engine.frameId) {
699707
this._currentFrameId = this._engine.frameId;
700708
this._createBufferOnWrite = false;
701709
if (this._buffers && this._buffers.length > 0) {
@@ -1230,7 +1238,7 @@ export class UniformBuffer {
12301238
uniformBuffers.pop();
12311239
}
12321240

1233-
if (this._engine._features.trackUbosInFrame && this._buffers) {
1241+
if (this._trackUBOsInFrame && this._buffers) {
12341242
for (let i = 0; i < this._buffers.length; ++i) {
12351243
const buffer = this._buffers[i][0];
12361244
this._engine._releaseBuffer(buffer);

packages/dev/core/src/Misc/snapshotRenderingHelper.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { Constants } from "core/Engines/constants";
1616
import { BindMorphTargetParameters } from "core/Materials/materialHelper.functions";
1717
import { ScenePerformancePriority } from "core/scene";
1818
import { Logger } from "core/Misc/logger";
19+
import { FrameGraphBaseLayerTask } from "../FrameGraph/Tasks/Layers/baseLayerTask";
1920

2021
/**
2122
* Options for the snapshot rendering helper
@@ -314,15 +315,15 @@ export class SnapshotRenderingHelper {
314315

315316
/**
316317
* Update the meshes used in an effect layer to ensure that snapshot rendering works correctly for these meshes in this layer.
317-
* @param effectLayer The effect layer
318-
* @param autoUpdate If true, the helper will automatically update the effect layer meshes with each frame. If false, you'll need to call this method manually when the camera or layer meshes move or rotate.
318+
* @param layer The effect layer or frame graph layer
319+
* @param autoUpdate If true, the helper will automatically update the meshes of the layer with each frame. If false, you'll need to call this method manually when the camera or layer meshes move or rotate.
319320
*/
320-
public updateMeshesForEffectLayer(effectLayer: EffectLayer, autoUpdate = true) {
321+
public updateMeshesForEffectLayer(layer: EffectLayer | FrameGraphBaseLayerTask, autoUpdate = true) {
321322
if (!this._engine.isWebGPU) {
322323
return;
323324
}
324325

325-
const renderPassId = effectLayer.mainTexture.renderPassId;
326+
const renderPassId = layer instanceof FrameGraphBaseLayerTask ? layer.objectRendererForLayer.objectRenderer.renderPassId : layer.mainTexture.renderPassId;
326327

327328
if (autoUpdate) {
328329
this._onBeforeRenderObserverUpdateLayer = this._scene.onBeforeRenderObservable.add(() => {
@@ -355,7 +356,8 @@ export class SnapshotRenderingHelper {
355356
return;
356357
}
357358

358-
const sceneTransformationMatrix = this._scene.getTransformMatrix();
359+
const sceneTransformationMatrix =
360+
this._scene.objectRenderers.find((renderer) => renderer.renderPassId === renderPassId)?.activeCamera?.getTransformationMatrix() ?? this._scene.getTransformMatrix();
359361

360362
for (let i = 0; i < this._scene.meshes.length; ++i) {
361363
const mesh = this._scene.meshes[i];

packages/dev/core/src/Rendering/objectRenderer.ts

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
1-
import type { Nullable, Immutable, Camera, Scene, AbstractMesh, SubMesh, Material, IParticleSystem, InstancedMesh, BoundingBox, BoundingBoxRenderer } from "core/index";
1+
import type {
2+
Nullable,
3+
Immutable,
4+
Camera,
5+
Scene,
6+
AbstractMesh,
7+
SubMesh,
8+
Material,
9+
IParticleSystem,
10+
InstancedMesh,
11+
BoundingBox,
12+
BoundingBoxRenderer,
13+
UniformBuffer,
14+
} from "core/index";
215
import { Observable } from "../Misc/observable";
316
import { RenderingManager } from "../Rendering/renderingManager";
417
import { Constants } from "../Engines/constants";
@@ -206,6 +219,9 @@ export class ObjectRenderer {
206219
protected _currentApplyByPostProcessSetting = false;
207220
protected _activeMeshes = new SmartArray<AbstractMesh>(256);
208221
protected _activeBoundingBoxes = new SmartArray<BoundingBox>(32);
222+
protected _useUBO: boolean;
223+
protected _sceneUBO: UniformBuffer;
224+
protected _currentSceneUBO: UniformBuffer;
209225

210226
/**
211227
* The options used by the object renderer
@@ -226,6 +242,9 @@ export class ObjectRenderer {
226242
}
227243

228244
this._name = value;
245+
if (this._sceneUBO) {
246+
this._sceneUBO.name = `Scene ubo for ${this.name}`;
247+
}
229248

230249
if (!this._scene) {
231250
return;
@@ -350,6 +369,10 @@ export class ObjectRenderer {
350369
constructor(name: string, scene: Scene, options?: ObjectRendererOptions) {
351370
this.name = name;
352371
this._scene = scene;
372+
this._useUBO = this._scene.getEngine().supportsUniformBuffers;
373+
if (this._useUBO) {
374+
this._sceneUBO = this._scene.createSceneUniformBuffer(`Scene ubo for ${this.name}`, false);
375+
}
353376

354377
this.renderList = [] as AbstractMesh[];
355378
this._renderPassIds = [];
@@ -509,14 +532,22 @@ export class ObjectRenderer {
509532

510533
this._currentSceneCamera = this._scene.activeCamera;
511534

535+
if (this._useUBO) {
536+
this._currentSceneUBO = this._scene.getSceneUniformBuffer();
537+
this._currentSceneUBO.unbindEffect();
538+
this._scene.setSceneUniformBuffer(this._sceneUBO);
539+
}
540+
512541
if (camera) {
513-
if (camera !== this._scene.activeCamera) {
514-
this._scene.setTransformMatrix(camera.getViewMatrix(), camera.getProjectionMatrix(true));
515-
this._scene.activeCamera = camera;
516-
}
542+
this._scene.setTransformMatrix(camera.getViewMatrix(), camera.getProjectionMatrix(true));
543+
this._scene.activeCamera = camera;
517544
engine.setViewport(camera.rigParent ? camera.rigParent.viewport : camera.viewport, viewportWidth, viewportHeight);
518545
}
519546

547+
if (this._useUBO) {
548+
this._scene.finalizeSceneUbo();
549+
}
550+
520551
this._defaultRenderListPrepared = false;
521552
}
522553

@@ -526,6 +557,10 @@ export class ObjectRenderer {
526557
public finishRender() {
527558
const scene = this._scene;
528559

560+
if (this._useUBO) {
561+
this._scene.setSceneUniformBuffer(this._currentSceneUBO);
562+
}
563+
529564
if (this._disableImageProcessing) {
530565
scene.imageProcessingConfiguration._applyByPostProcess = this._currentApplyByPostProcessSetting;
531566
}
@@ -896,6 +931,8 @@ export class ObjectRenderer {
896931
this._releaseRenderPassId();
897932

898933
this.renderList = null;
934+
this._sceneUBO?.dispose();
935+
this._sceneUBO = undefined as any;
899936

900937
this._scene.removeObjectRenderer(this);
901938
}

packages/dev/core/src/scene.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2729,10 +2729,11 @@ export class Scene implements IAnimatable, IClipPlanesHolder, IAssetContainer {
27292729
/**
27302730
* Creates a scene UBO
27312731
* @param name name of the uniform buffer (optional, for debugging purpose only)
2732+
* @param trackUBOsInFrame define if the UBOs should be tracked in the frame (default: undefined - will use the value from Engine._features.trackUbosInFrame)
27322733
* @returns a new ubo
27332734
*/
2734-
public createSceneUniformBuffer(name?: string): UniformBuffer {
2735-
const sceneUbo = new UniformBuffer(this._engine, undefined, false, name ?? "scene");
2735+
public createSceneUniformBuffer(name?: string, trackUBOsInFrame?: boolean): UniformBuffer {
2736+
const sceneUbo = new UniformBuffer(this._engine, undefined, false, name ?? "scene", undefined, trackUBOsInFrame);
27362737
sceneUbo.addUniform("viewProjection", 16);
27372738
sceneUbo.addUniform("view", 16);
27382739
sceneUbo.addUniform("projection", 16);

0 commit comments

Comments
 (0)