@@ -11,6 +11,7 @@ import type {
1111 WritableObject ,
1212 IShadowLight ,
1313 INodeRenderGraphCustomBlockDescription ,
14+ Immutable ,
1415} from "core/index" ;
1516import { Observable } from "../../Misc/observable" ;
1617import { NodeRenderGraphOutputBlock } from "./Blocks/outputBlock" ;
@@ -83,7 +84,12 @@ export class NodeRenderGraph {
8384 public attachedBlocks : NodeRenderGraphBlock [ ] = [ ] ;
8485
8586 /**
86- * Observable raised when the node render graph is built
87+ * Observable raised before the node render graph is built
88+ */
89+ public onBeforeBuildObservable = new Observable < FrameGraph > ( ) ;
90+
91+ /**
92+ * Observable raised after the node render graph is built
8793 * Note that this is the same observable as the one in the underlying FrameGraph!
8894 */
8995 public get onBuildObservable ( ) {
@@ -136,6 +142,13 @@ export class NodeRenderGraph {
136142 return this . _scene ;
137143 }
138144
145+ /**
146+ * Gets the options used to create this node render graph
147+ */
148+ public get options ( ) : Immutable < INodeRenderGraphCreateOptions > {
149+ return this . _options ;
150+ }
151+
139152 /**
140153 * Creates a new node render graph
141154 * @param name defines the name of the node render graph
@@ -163,9 +176,7 @@ export class NodeRenderGraph {
163176
164177 if ( options . rebuildGraphOnEngineResize ) {
165178 this . _resizeObserver = this . _engine . onResizeObservable . add ( async ( ) => {
166- this . build ( ) ;
167-
168- await this . whenReadyAsync ( ) ;
179+ await this . buildAsync ( false , true , false ) ;
169180 } ) ;
170181 }
171182 }
@@ -285,14 +296,29 @@ export class NodeRenderGraph {
285296 }
286297
287298 /**
288- * Build the final list of blocks that will be executed by the "execute" method
299+ * @deprecated Use buildAsync instead
289300 * @param dontBuildFrameGraph If the underlying frame graph should not be built (default: false)
290301 */
291- public build ( dontBuildFrameGraph = false ) {
302+ public build ( dontBuildFrameGraph = false ) : void {
303+ void this . buildAsync ( dontBuildFrameGraph , false , false ) ;
304+ }
305+
306+ /**
307+ * Build the final list of blocks that will be executed by the "execute" method.
308+ * It also builds the underlying frame graph unless specified otherwise.
309+ * @param dontBuildFrameGraph If the underlying frame graph should not be built (default: false)
310+ * @param waitForReadiness If the method should wait for the frame graph to be ready before resolving (default: true). Note that this parameter has no effect if "dontBuildFrameGraph" is true.
311+ * @param setAsSceneFrameGraph If the built frame graph must be set as the scene's frame graph (default: true)
312+ */
313+ public async buildAsync ( dontBuildFrameGraph = false , waitForReadiness = true , setAsSceneFrameGraph = true ) : Promise < void > {
292314 if ( ! this . outputBlock ) {
293315 throw new Error ( "You must define the outputBlock property before building the node render graph" ) ;
294316 }
295317
318+ if ( setAsSceneFrameGraph ) {
319+ this . _scene . frameGraph = this . _frameGraph ;
320+ }
321+
296322 this . _initializeBlock ( this . outputBlock ) ;
297323
298324 this . _frameGraph . clear ( ) ;
@@ -306,6 +332,8 @@ export class NodeRenderGraph {
306332 this . _autoFillExternalInputs ( ) ;
307333 }
308334
335+ this . onBeforeBuildObservable . notifyObservers ( this . _frameGraph ) ;
336+
309337 // Make sure that one of the object renderer is flagged as the main object renderer
310338 const objectRendererBlocks = this . getBlocksByPredicate < NodeRenderGraphBaseObjectRendererBlock > ( ( block ) => block instanceof NodeRenderGraphBaseObjectRendererBlock ) ;
311339 if ( objectRendererBlocks . length > 0 && ! objectRendererBlocks . find ( ( block ) => block . isMainObjectRenderer ) ) {
@@ -316,7 +344,7 @@ export class NodeRenderGraph {
316344 this . outputBlock . build ( state ) ;
317345
318346 if ( ! dontBuildFrameGraph ) {
319- this . _frameGraph . build ( ) ;
347+ await this . _frameGraph . buildAsync ( waitForReadiness ) ;
320348 }
321349 } finally {
322350 this . _buildId = NodeRenderGraph . _BuildIdGenerator ++ ;
@@ -368,16 +396,14 @@ export class NodeRenderGraph {
368396 * Returns a promise that resolves when the node render graph is ready to be executed
369397 * This method must be called after the graph has been built (NodeRenderGraph.build called)!
370398 * @param timeStep Time step in ms between retries (default is 16)
371- * @param maxTimeout Maximum time in ms to wait for the graph to be ready (default is 5000 )
399+ * @param maxTimeout Maximum time in ms to wait for the graph to be ready (default is 10000 )
372400 * @returns The promise that resolves when the graph is ready
373401 */
374402 // eslint-disable-next-line @typescript-eslint/promise-function-async, no-restricted-syntax
375- public whenReadyAsync ( timeStep = 16 , maxTimeout = 5000 ) : Promise < void > {
403+ public async whenReadyAsync ( timeStep = 16 , maxTimeout = 10000 ) : Promise < void > {
376404 this . _frameGraph . pausedExecution = true ;
377- // eslint-disable-next-line github/no-then
378- return this . _frameGraph . whenReadyAsync ( timeStep , maxTimeout ) . then ( ( ) => {
379- this . _frameGraph . pausedExecution = false ;
380- } ) ;
405+ await this . _frameGraph . whenReadyAsync ( timeStep , maxTimeout ) ;
406+ this . _frameGraph . pausedExecution = false ;
381407 }
382408
383409 /**
@@ -667,6 +693,7 @@ export class NodeRenderGraph {
667693
668694 /**
669695 * Makes a duplicate of the current node render graph.
696+ * Note that you should call buildAsync() on the returned graph to make it usable.
670697 * @param name defines the name to use for the new node render graph
671698 * @returns the new node render graph
672699 */
@@ -678,7 +705,6 @@ export class NodeRenderGraph {
678705
679706 clone . parseSerializedObject ( serializationObject ) ;
680707 clone . _buildId = this . _buildId ;
681- clone . build ( ) ;
682708
683709 return clone ;
684710 }
@@ -747,11 +773,11 @@ export class NodeRenderGraph {
747773 * @param nodeRenderGraphOptions defines options to use when creating the node render graph
748774 * @returns a new NodeRenderGraph
749775 */
750- public static CreateDefault ( name : string , scene : Scene , nodeRenderGraphOptions ?: INodeRenderGraphCreateOptions ) : NodeRenderGraph {
776+ public static async CreateDefaultAsync ( name : string , scene : Scene , nodeRenderGraphOptions ?: INodeRenderGraphCreateOptions ) : Promise < NodeRenderGraph > {
751777 const renderGraph = new NodeRenderGraph ( name , scene , nodeRenderGraphOptions ) ;
752778
753779 renderGraph . setToDefault ( ) ;
754- renderGraph . build ( ) ;
780+ await renderGraph . buildAsync ( false , true , false ) ;
755781
756782 return renderGraph ;
757783 }
@@ -769,7 +795,7 @@ export class NodeRenderGraph {
769795
770796 renderGraph . parseSerializedObject ( source ) ;
771797 if ( ! skipBuild ) {
772- renderGraph . build ( ) ;
798+ void renderGraph . buildAsync ( ) ;
773799 }
774800
775801 return renderGraph ;
@@ -793,12 +819,12 @@ export class NodeRenderGraph {
793819 skipBuild : boolean = true
794820 ) : Promise < NodeRenderGraph > {
795821 if ( snippetId === "_BLANK" ) {
796- return Promise . resolve ( NodeRenderGraph . CreateDefault ( "blank" , scene , nodeRenderGraphOptions ) ) ;
822+ return NodeRenderGraph . CreateDefaultAsync ( "blank" , scene , nodeRenderGraphOptions ) ;
797823 }
798824
799825 return new Promise ( ( resolve , reject ) => {
800826 const request = new WebRequest ( ) ;
801- request . addEventListener ( "readystatechange" , ( ) => {
827+ request . addEventListener ( "readystatechange" , async ( ) => {
802828 if ( request . readyState == 4 ) {
803829 if ( request . status == 200 ) {
804830 const snippet = JSON . parse ( JSON . parse ( request . responseText ) . jsonPayload ) ;
@@ -813,7 +839,7 @@ export class NodeRenderGraph {
813839
814840 try {
815841 if ( ! skipBuild ) {
816- nodeRenderGraph . build ( ) ;
842+ await nodeRenderGraph . buildAsync ( ) ;
817843 }
818844 resolve ( nodeRenderGraph ) ;
819845 } catch ( err ) {
0 commit comments