From 2fab76f49faf69f93d263b24f4774a418702db9b Mon Sep 17 00:00:00 2001 From: sebavan Date: Fri, 21 Oct 2022 00:35:57 +0200 Subject: [PATCH 1/2] Update Pressure Observer to latest spec Former-commit-id: 53cda82ee5672cd279c2fcc384254da4fab7bdc6 --- .../dev/core/src/LibDeclarations/browser.d.ts | 34 ++++++++ .../performanceViewerCollectionStrategies.ts | 64 +++++++++++++-- packages/dev/core/src/Misc/computePressure.ts | 82 ------------------- packages/dev/core/src/Misc/index.ts | 2 +- .../core/src/Misc/pressureObserverWrapper.ts | 70 ++++++++++++++++ packages/dev/core/src/scene.ts | 28 ------- .../performanceViewerComponent.tsx | 18 +++- 7 files changed, 175 insertions(+), 123 deletions(-) delete mode 100644 packages/dev/core/src/Misc/computePressure.ts create mode 100644 packages/dev/core/src/Misc/pressureObserverWrapper.ts diff --git a/packages/dev/core/src/LibDeclarations/browser.d.ts b/packages/dev/core/src/LibDeclarations/browser.d.ts index be9e4f93a67..2a53fd299ec 100644 --- a/packages/dev/core/src/LibDeclarations/browser.d.ts +++ b/packages/dev/core/src/LibDeclarations/browser.d.ts @@ -88,3 +88,37 @@ declare var OffscreenCanvas: { prototype: OffscreenCanvas; new (width: number, height: number): OffscreenCanvas; }; + +// Experimental Pressure API https://wicg.github.io/compute-pressure/ +type PressureSource = "cpu"; + +type PressureState = "nominal" | "fair" | "serious" | "critical"; + +type PressureFactor = "thermal" | "power-supply"; + +interface PressureRecord { + source: PressureSource; + state: PressureState; + factors: ReadonlyArray; + time: number; +} + +interface PressureObserver { + observe(source: PressureSource): void; + unobserve(source: PressureSource): void; + disconnect(): void; + takeRecords(): Array; +} + +interface PressureObserverOptions { + sampleRate?: number; +} + +type PressureUpdateCallback = (changes: Array, observer: PressureObserver) => void; + +declare const PressureObserver: { + prototype: PressureObserver; + new (callback: PressureUpdateCallback, options?: PressureObserverOptions): PressureObserver; + + supportedSources: ReadonlyArray; +}; \ No newline at end of file diff --git a/packages/dev/core/src/Misc/PerformanceViewer/performanceViewerCollectionStrategies.ts b/packages/dev/core/src/Misc/PerformanceViewer/performanceViewerCollectionStrategies.ts index 1209606a0ca..d3d31811d28 100644 --- a/packages/dev/core/src/Misc/PerformanceViewer/performanceViewerCollectionStrategies.ts +++ b/packages/dev/core/src/Misc/PerformanceViewer/performanceViewerCollectionStrategies.ts @@ -2,6 +2,8 @@ import { EngineInstrumentation } from "../../Instrumentation/engineInstrumentati import type { Scene } from "../../scene"; import { PrecisionDate } from "../precisionDate"; import { SceneInstrumentation } from "../../Instrumentation/sceneInstrumentation"; +import { PressureObserverWrapper } from "../pressureObserverWrapper"; +import type { Nullable } from "../../types"; /** * Defines the general structure of what is necessary for a collection strategy. @@ -47,20 +49,64 @@ export class PerfCollectionStrategy { } /** - * Gets the initializer for the strategy used for collection of cpu utilization metrics. - * Needs the experimental compute pressure API. - * @returns the initializer for the cpu utilization strategy + * Gets the initializer for the strategy used for collection of thermal utilization metrics. + * Needs the experimental pressure API. + * @returns the initializer for the thermal utilization strategy */ - public static CpuStrategy(): PerfStrategyInitialization { - return (scene) => { + public static ThermalStrategy(): PerfStrategyInitialization { + return this._PressureStrategy("Thermal utilization", "thermal"); + } + + /** + * Gets the initializer for the strategy used for collection of power supply utilization metrics. + * Needs the experimental pressure API. + * @returns the initializer for the power supply utilization strategy + */ + public static PowerSupplyStrategy(): PerfStrategyInitialization { + return this._PressureStrategy("Power supply utilization", "power-supply"); + } + + /** + * Gets the initializer for the strategy used for collection of pressure metrics. + * Needs the experimental pressure API. + * @returns the initializer for the pressure strategy + */ + public static PressureStrategy(): PerfStrategyInitialization { + return this._PressureStrategy("Pressure"); + } + + private static _PressureStrategy(name: string, factor: Nullable = null): PerfStrategyInitialization { + return () => { let value = 0; - const computePressureObserver = scene.onComputePressureChanged.add((update) => { - value = update.cpuUtilization; + + const wrapper = new PressureObserverWrapper(); + wrapper.observe("cpu"); + + wrapper.onPressureChanged.add((update) => { + for (const record of update) { + if ((factor && record.factors.includes(factor)) || (!factor && record.factors.length === 0)) { + // Let s consider each step being 25% of the total pressure. + switch (record.state) { + case "nominal": + value = 0; + break; + case "fair": + value = 0.25; + break; + case "serious": + value = 0.5; + break; + case "critical": + value = 1; + break; + } + } + } }); return { - id: "CPU utilization", + id: name, getData: () => value, - dispose: () => scene.onComputePressureChanged.remove(computePressureObserver), + dispose: () => wrapper.dispose(), }; }; } diff --git a/packages/dev/core/src/Misc/computePressure.ts b/packages/dev/core/src/Misc/computePressure.ts deleted file mode 100644 index 14ed79cb450..00000000000 --- a/packages/dev/core/src/Misc/computePressure.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { IsWindowObjectExist } from "./domManagement"; - -/** - * A wrapper for the experimental compute pressure api which allows a callback to be called whenever certain thresholds are met. - */ -export class ComputePressureObserverWrapper { - private _observer: any; - /** - * A compute pressure observer will call this callback, whenever these thresholds are met. - * @param callback The callback that is called whenever thresholds are met. - * @param thresholds An object containing the thresholds used to decide what value to to return for each update property (average of start and end of a threshold boundary). - */ - constructor(callback: (update: IComputePressureData) => void, thresholds: IComputePressureThresholds) { - if (ComputePressureObserverWrapper.IsAvailable) { - this._observer = new (window).ComputePressureObserver(callback, thresholds); - } - } - - /** - * Returns true if ComputePressureObserver is available for use, false otherwise. - */ - public static get IsAvailable() { - return IsWindowObjectExist() && "ComputePressureObserver" in window && (window).ComputePressureObserver?.supportedSources?.includes("cpu"); - } - - /** - * Method that must be called to begin observing changes, and triggering callbacks. - * @param source defines the source to observe - */ - observe(source: IComputePressureSource): void { - this._observer?.observe && - this._observer?.observe(source).catch(() => { - // Ignore error - }); - } - - /** - * Method that must be called to stop observing changes and triggering callbacks (cleanup function). - * @param source defines the source to unobserve - */ - unobserve(source: IComputePressureSource): void { - try { - this._observer?.unobserve && this._observer?.unobserve(source); - } catch { - // Ignore error - } - } -} - -/** - * An interface defining the shape of the thresholds parameter in the experimental compute pressure api - */ -export interface IComputePressureThresholds { - /** - * Thresholds to make buckets out of for the cpu utilization, the average between the start and end points of a threshold will be returned to the callback. - */ - cpuUtilizationThresholds: number[]; - /** - * Thresholds to make buckets out of for the cpu speed, the average between the start and end points of a threshold will be returned to the callback. - * 0.5 represents base speed. - */ - cpuSpeedThresholds: number[]; -} - -/** - * An interface defining the shape of the data sent to the callback in the compute pressure observer. - */ -export interface IComputePressureData { - /** - * The cpu utilization which will be a number between 0.0 and 1.0. - */ - cpuUtilization: number; - /** - * The cpu speed which will be a number between 0.0 and 1.0. - */ - cpuSpeed: number; -} - -/** - * The possible sources for the compute pressure observer. - */ -export type IComputePressureSource = "cpu"; diff --git a/packages/dev/core/src/Misc/index.ts b/packages/dev/core/src/Misc/index.ts index a9aa3280d00..c9cc76be804 100644 --- a/packages/dev/core/src/Misc/index.ts +++ b/packages/dev/core/src/Misc/index.ts @@ -55,7 +55,7 @@ export * from "./timer"; export * from "./copyTools"; export * from "./reflector"; export * from "./domManagement"; -export * from "./computePressure"; +export * from "./pressureObserverWrapper"; export * from "./PerformanceViewer/index"; export * from "./coroutine"; export * from "./guid"; diff --git a/packages/dev/core/src/Misc/pressureObserverWrapper.ts b/packages/dev/core/src/Misc/pressureObserverWrapper.ts new file mode 100644 index 00000000000..596dbe704dc --- /dev/null +++ b/packages/dev/core/src/Misc/pressureObserverWrapper.ts @@ -0,0 +1,70 @@ +import type { Nullable } from "../types"; +import { Observable } from "./observable"; + +/** + * A wrapper for the experimental pressure api which allows a callback to be called whenever certain thresholds are met. + */ +export class PressureObserverWrapper { + private _observer: Nullable = null; + private _currentState: PressureRecord[] = []; + + /** + * An event triggered when the cpu usage/speed meets certain thresholds. + * Note: pressure is an experimental API. + */ + public onPressureChanged = new Observable(); + + /** + * A pressure observer will call this callback, whenever these thresholds are met. + * @param options An object containing the thresholds used to decide what value to to return for each update property (average of start and end of a threshold boundary). + */ + constructor(options?: PressureObserverOptions) { + if (PressureObserverWrapper.IsAvailable) { + this._observer = new PressureObserver((update) => { + this._currentState = update; + this.onPressureChanged.notifyObservers(update); + }, options); + } + } + + /** + * Returns true if PressureObserver is available for use, false otherwise. + */ + public static get IsAvailable() { + return typeof PressureObserver !== "undefined" && PressureObserver.supportedSources.includes("cpu"); + } + + /** + * Method that must be called to begin observing changes, and triggering callbacks. + * @param source defines the source to observe + */ + observe(source: PressureSource): void { + try { + this._observer?.observe(source); + this.onPressureChanged.notifyObservers(this._currentState); + } catch { + // Ignore error + } + } + + /** + * Method that must be called to stop observing changes and triggering callbacks (cleanup function). + * @param source defines the source to unobserve + */ + unobserve(source: PressureSource): void { + try { + this._observer?.unobserve(source); + } catch { + // Ignore error + } + } + + /** + * Release the associated resources. + */ + dispose() { + this._observer?.disconnect(); + this._observer = null; + this.onPressureChanged.clear(); + } +} diff --git a/packages/dev/core/src/scene.ts b/packages/dev/core/src/scene.ts index 78445c8f991..d88502d895b 100644 --- a/packages/dev/core/src/scene.ts +++ b/packages/dev/core/src/scene.ts @@ -60,8 +60,6 @@ import { ReadFile, RequestFile, LoadFile } from "./Misc/fileTools"; import type { IClipPlanesHolder } from "./Misc/interfaces/iClipPlanesHolder"; import type { IPointerEvent } from "./Events/deviceInputEvents"; import { LightConstants } from "./Lights/lightConstants"; -import type { IComputePressureData } from "./Misc/computePressure"; -import { ComputePressureObserverWrapper } from "./Misc/computePressure"; import { _ObserveArray } from "./Misc/arrayTools"; declare type Ray = import("./Culling/ray").Ray; @@ -1622,20 +1620,6 @@ export class Scene extends AbstractScene implements IAnimatable, IClipPlanesHold if (!options || !options.virtual) { this._engine.onNewSceneAddedObservable.notifyObservers(this); } - - if (ComputePressureObserverWrapper.IsAvailable) { - this._computePressureObserver = new ComputePressureObserverWrapper( - (update) => { - this.onComputePressureChanged.notifyObservers(update); - }, - { - // Thresholds divide the interval [0.0 .. 1.0] into ranges. - cpuUtilizationThresholds: [0.25, 0.5, 0.75, 0.9], - cpuSpeedThresholds: [0.5], - } - ); - this._computePressureObserver.observe("cpu"); - } } /** @@ -4748,10 +4732,6 @@ export class Scene extends AbstractScene implements IAnimatable, IClipPlanesHold this.onPreKeyboardObservable.clear(); this.onKeyboardObservable.clear(); this.onActiveCameraChanged.clear(); - this.onComputePressureChanged.clear(); - - this._computePressureObserver?.unobserve("cpu"); - this._computePressureObserver = undefined; this.detachControl(); @@ -5405,12 +5385,4 @@ export class Scene extends AbstractScene implements IAnimatable, IClipPlanesHold public getPerfCollector(): PerformanceViewerCollector { throw _WarnImport("performanceViewerSceneExtension"); } - - private _computePressureObserver: ComputePressureObserverWrapper | undefined; - - /** - * An event triggered when the cpu usage/speed meets certain thresholds. - * Note: Compute pressure is an experimental API. - */ - public onComputePressureChanged = new Observable(); } diff --git a/packages/dev/inspector/src/components/actionTabs/tabs/performanceViewer/performanceViewerComponent.tsx b/packages/dev/inspector/src/components/actionTabs/tabs/performanceViewer/performanceViewerComponent.tsx index 0df093d9cd9..8387eaffa60 100644 --- a/packages/dev/inspector/src/components/actionTabs/tabs/performanceViewer/performanceViewerComponent.tsx +++ b/packages/dev/inspector/src/components/actionTabs/tabs/performanceViewer/performanceViewerComponent.tsx @@ -12,7 +12,7 @@ import { Tools } from "core/Misc/tools"; import "core/Misc/PerformanceViewer/performanceViewerSceneExtension"; import { Inspector } from "../../../../inspector"; import { PerformanceViewerPopupComponent } from "./performanceViewerPopupComponent"; -import { ComputePressureObserverWrapper } from "core/Misc/computePressure"; +import { PressureObserverWrapper } from "core/Misc/pressureObserverWrapper"; import "./scss/performanceViewer.scss"; @@ -151,9 +151,21 @@ export const PerformanceViewerComponent: React.FC { perfCollector.addCollectionStrategies(...defaultStrategiesList); - if (ComputePressureObserverWrapper.IsAvailable) { + if (PressureObserverWrapper.IsAvailable) { + // Do not enable for now as the Pressure API does not + // report factors at the moment. + // perfCollector.addCollectionStrategies({ + // strategyCallback: PerfCollectionStrategy.ThermalStrategy(), + // category: IPerfMetadataCategory.FrameSteps, + // hidden: true, + // }); + // perfCollector.addCollectionStrategies({ + // strategyCallback: PerfCollectionStrategy.PowerSupplyStrategy(), + // category: IPerfMetadataCategory.FrameSteps, + // hidden: true, + // }); perfCollector.addCollectionStrategies({ - strategyCallback: PerfCollectionStrategy.CpuStrategy(), + strategyCallback: PerfCollectionStrategy.PressureStrategy(), category: IPerfMetadataCategory.FrameSteps, hidden: true, }); From c7b1c9c68e3d96b93e04d0d5a144758b82d9de22 Mon Sep 17 00:00:00 2001 From: sebavan Date: Fri, 21 Oct 2022 19:21:10 +0200 Subject: [PATCH 2/2] format Former-commit-id: 3c986208c289219c3838c869c405b50fd554d877 --- packages/dev/core/src/LibDeclarations/browser.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dev/core/src/LibDeclarations/browser.d.ts b/packages/dev/core/src/LibDeclarations/browser.d.ts index 2a53fd299ec..8361145bbab 100644 --- a/packages/dev/core/src/LibDeclarations/browser.d.ts +++ b/packages/dev/core/src/LibDeclarations/browser.d.ts @@ -121,4 +121,4 @@ declare const PressureObserver: { new (callback: PressureUpdateCallback, options?: PressureObserverOptions): PressureObserver; supportedSources: ReadonlyArray; -}; \ No newline at end of file +};