UNPKG

polygonjs-engine

Version:

node-based webgl 3D engine https://polygonjs.com

205 lines (185 loc) 6.58 kB
import {BaseNodeType} from '../_Base'; import {Poly} from '../../Poly'; import {CorePerformance} from '../../../core/performance/CorePerformance'; import {NodeCookPerformanceformanceController} from './cook/PerformanceController'; import {ContainerMap} from '../../containers/utils/ContainerMap'; import {NodeContext} from '../../poly/NodeContext'; import {ContainableMap} from '../../containers/utils/ContainableMap'; import {CoreGraphNode} from '../../../core/graph/CoreGraphNode'; import {CoreGraphNodeId} from '../../../core/graph/CoreGraph'; export type OnCookCompleteHook = (node: BaseNodeType) => void; export class NodeCookController<NC extends NodeContext> { private _core_performance: CorePerformance; private _cooking: boolean = false; private _cooking_dirty_timestamp: number | undefined; private _performance_controller: NodeCookPerformanceformanceController = new NodeCookPerformanceformanceController( this ); constructor(private node: BaseNodeType) { this._core_performance = this.node.scene().performance; } get performance_record_started() { return this._core_performance.started(); } // Disallowing inputs evaluation is important for switch nodes (such as SOP and COP) // that should not evaluate all inputs, but only a single one, depending on a param value // currently only for switch SOP and COP private _inputs_evaluation_required: boolean = true; disallow_inputs_evaluation() { this._inputs_evaluation_required = false; } get is_cooking(): boolean { return this._cooking === true; } private _init_cooking_state() { this._cooking = true; this._cooking_dirty_timestamp = this.node.dirtyController.dirtyTimestamp(); } private _start_cook_if_no_errors(input_contents: ContainableMap[NC][]) { if (this.node.states.error.active()) { this.end_cook(); } else { try { this._performance_controller.record_cook_start(); this.node.cook(input_contents); } catch (e) { this.node.states.error.set(`node internal error: '${e}'.`); Poly.warn(e); this.end_cook(); } } } async cook_main() { if (this.is_cooking) { return; } this._init_cooking_state(); this.node.states.error.clear(); this.node.scene().cookController.add_node(this.node); let input_contents: ContainableMap[NC][]; if (this._inputs_evaluation_required) { input_contents = await this._evaluate_inputs(); } else { input_contents = []; } if (this.node.params.params_eval_required()) { await this._evaluate_params(); } this._start_cook_if_no_errors(input_contents); } async cook_main_without_inputs() { this.node.scene().cookController.add_node(this.node); if (this.is_cooking) { // TODO: // this seems to happen because when we flush the cooker queue, // some graph nodes will trigger more updates, which will then make dependent nodes // dirty again Poly.warn('cook_main_without_inputs already cooking', this.node.fullPath()); return; } this._init_cooking_state(); this.node.states.error.clear(); if (this.node.params.params_eval_required()) { await this._evaluate_params(); } this._start_cook_if_no_errors([]); } end_cook(message?: string | null) { this._finalize_cook_performance(); const dirty_timestamp = this.node.dirtyController.dirtyTimestamp(); if (dirty_timestamp == null || dirty_timestamp === this._cooking_dirty_timestamp) { this.node.removeDirtyState(); this._terminate_cook_process(); } else { Poly.log('COOK AGAIN', dirty_timestamp, this._cooking_dirty_timestamp, this.node.fullPath()); this._cooking = false; this.cook_main(); } } private _terminate_cook_process() { if (this.is_cooking) { this._cooking = false; // setTimeout(this.node.container_controller.notify_requesters.bind(this.node.container_controller), 0); this.node.container_controller.notify_requesters(); this._run_on_cook_complete_hooks(); } } private async _evaluate_inputs(): Promise<ContainableMap[NC][]> { this._performance_controller.record_inputs_start(); let input_containers: (ContainerMap[NC] | null)[] = []; const io_inputs = this.node.io.inputs; if (this._inputs_evaluation_required) { if (io_inputs.is_any_input_dirty()) { input_containers = await io_inputs.eval_required_inputs(); } else { input_containers = await io_inputs.containers_without_evaluation(); } } const inputs = io_inputs.inputs(); const input_contents: ContainableMap[NC][] = []; let input_container: ContainerMap[NC] | null; for (let i = 0; i < inputs.length; i++) { input_container = input_containers[i]; if (input_container) { if (io_inputs.clone_required(i)) { input_contents[i] = input_container.coreContentCloned() as ContainableMap[NC]; } else { input_contents[i] = input_container.coreContent() as ContainableMap[NC]; } } } this._performance_controller.record_inputs_end(); return input_contents; } private async _evaluate_params() { this._performance_controller.record_params_start(); await this.node.params.eval_all(); this._performance_controller.record_params_end(); } // // // PERFORMANCE // // get cooks_count(): number { return this._performance_controller.cooks_count; } get cook_time(): number { return this._performance_controller.data.cook_time; } private _finalize_cook_performance() { if (!this._core_performance.started()) { return; } this._performance_controller.record_cook_end(); this._core_performance.record_node_cook_data(this.node, this._performance_controller.data); } // // // HOOK // // private _on_cook_complete_hook_ids: CoreGraphNodeId[] | undefined; private _on_cook_complete_hooks: OnCookCompleteHook[] | undefined; add_on_cook_complete_hook(core_graph_node: CoreGraphNode, callback: OnCookCompleteHook) { this._on_cook_complete_hook_ids = this._on_cook_complete_hook_ids || []; this._on_cook_complete_hooks = this._on_cook_complete_hooks || []; this._on_cook_complete_hook_ids.push(core_graph_node.graphNodeId()); this._on_cook_complete_hooks.push(callback); } remove_on_cook_complete_hook(core_graph_node: CoreGraphNode) { if (!this._on_cook_complete_hook_ids || !this._on_cook_complete_hooks) { return; } const index = this._on_cook_complete_hook_ids?.indexOf(core_graph_node.graphNodeId()); this._on_cook_complete_hook_ids.splice(index, 1); this._on_cook_complete_hooks.splice(index, 1); } private _run_on_cook_complete_hooks() { if (this._on_cook_complete_hooks) { for (let hook of this._on_cook_complete_hooks) { hook(this.node); } } } }