Skip to content

Commit

Permalink
Merge pull request #13146 from sebavan/master
Browse files Browse the repository at this point in the history
Update Pressure Observer to latest version of the WICG spec

Former-commit-id: 11fda9004a0aca651f6a82f50beae41a36257cde
  • Loading branch information
sebavan authored Oct 21, 2022
2 parents 74ab8bf + c7b1c9c commit bf41265
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 123 deletions.
34 changes: 34 additions & 0 deletions packages/dev/core/src/LibDeclarations/browser.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<PressureFactor>;
time: number;
}

interface PressureObserver {
observe(source: PressureSource): void;
unobserve(source: PressureSource): void;
disconnect(): void;
takeRecords(): Array<PressureRecord>;
}

interface PressureObserverOptions {
sampleRate?: number;
}

type PressureUpdateCallback = (changes: Array<PressureRecord>, observer: PressureObserver) => void;

declare const PressureObserver: {
prototype: PressureObserver;
new (callback: PressureUpdateCallback, options?: PressureObserverOptions): PressureObserver;

supportedSources: ReadonlyArray<PressureSource>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<PressureFactor> = 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(),
};
};
}
Expand Down
82 changes: 0 additions & 82 deletions packages/dev/core/src/Misc/computePressure.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/dev/core/src/Misc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
70 changes: 70 additions & 0 deletions packages/dev/core/src/Misc/pressureObserverWrapper.ts
Original file line number Diff line number Diff line change
@@ -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<PressureObserver> = 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<PressureRecord[]>();

/**
* 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();
}
}
28 changes: 0 additions & 28 deletions packages/dev/core/src/scene.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
}
}

/**
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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<IComputePressureData>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -151,9 +151,21 @@ export const PerformanceViewerComponent: React.FC<IPerformanceViewerComponentPro

const addStrategies = (perfCollector: PerformanceViewerCollector) => {
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,
});
Expand Down

0 comments on commit bf41265

Please sign in to comment.