Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Pressure Observer to latest version of the WICG spec #13146

Merged
merged 4 commits into from
Oct 21, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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