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

WebGPU: Fix engine initialization #13541

Merged
merged 1 commit into from
Feb 16, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
74 changes: 33 additions & 41 deletions packages/dev/core/src/Engines/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,43 +596,9 @@ export class Engine extends ThinEngine {
if ((<any>canvasOrContext).getContext) {
const canvas = <HTMLCanvasElement>canvasOrContext;

this._sharedInit(canvas, !!options.doNotHandleTouchAction, options.audioEngine!);

if (IsDocumentAvailable()) {
// Fullscreen
this._onFullscreenChange = () => {
this.isFullscreen = !!document.fullscreenElement;

// Pointer lock
if (this.isFullscreen && this._pointerLockRequested && canvas) {
Engine._RequestPointerlock(canvas);
}
};

document.addEventListener("fullscreenchange", this._onFullscreenChange, false);
document.addEventListener("webkitfullscreenchange", this._onFullscreenChange, false);

// Pointer lock
this._onPointerLockChange = () => {
this.isPointerLock = document.pointerLockElement === canvas;
};

document.addEventListener("pointerlockchange", this._onPointerLockChange, false);
document.addEventListener("webkitpointerlockchange", this._onPointerLockChange, false);

// Create Audio Engine if needed.
if (!Engine.audioEngine && options.audioEngine && Engine.AudioEngineFactory) {
Engine.audioEngine = Engine.AudioEngineFactory(this.getRenderingCanvas(), this.getAudioContext(), this.getAudioDestination());
}
}
this._sharedInit(canvas);

this._connectVREvents();

this.enableOfflineSupport = Engine.OfflineProviderFactory !== undefined;

this._deterministicLockstep = !!options.deterministicLockstep;
this._lockstepMaxSteps = options.lockstepMaxSteps || 0;
this._timeStep = options.timeStep || 1 / 60;
}

// Load WebVR Devices
Expand All @@ -651,11 +617,9 @@ export class Engine extends ThinEngine {
/**
* Shared initialization across engines types.
* @param canvas The canvas associated with this instance of the engine.
* @param doNotHandleTouchAction Defines that engine should ignore modifying touch action attribute and style
* @param audioEngine Defines if an audio engine should be created by default
*/
protected _sharedInit(canvas: HTMLCanvasElement, doNotHandleTouchAction: boolean, audioEngine: boolean) {
super._sharedInit(canvas, doNotHandleTouchAction, audioEngine);
protected _sharedInit(canvas: HTMLCanvasElement) {
super._sharedInit(canvas);

this._onCanvasFocus = () => {
this.onCanvasFocusObservable.notifyObservers(this);
Expand Down Expand Up @@ -705,14 +669,42 @@ export class Engine extends ThinEngine {

canvas.addEventListener("pointerout", this._onCanvasPointerOut);

if (!doNotHandleTouchAction) {
if (this._creationOptions.doNotHandleTouchAction) {
this._disableTouchAction();
}

// Create Audio Engine if needed.
if (!Engine.audioEngine && audioEngine && Engine.AudioEngineFactory) {
if (!Engine.audioEngine && this._creationOptions.audioEngine && Engine.AudioEngineFactory) {
Engine.audioEngine = Engine.AudioEngineFactory(this.getRenderingCanvas(), this.getAudioContext(), this.getAudioDestination());
}
if (IsDocumentAvailable()) {
// Fullscreen
this._onFullscreenChange = () => {
this.isFullscreen = !!document.fullscreenElement;

// Pointer lock
if (this.isFullscreen && this._pointerLockRequested && canvas) {
Engine._RequestPointerlock(canvas);
}
};

document.addEventListener("fullscreenchange", this._onFullscreenChange, false);
document.addEventListener("webkitfullscreenchange", this._onFullscreenChange, false);

// Pointer lock
this._onPointerLockChange = () => {
this.isPointerLock = document.pointerLockElement === canvas;
};

document.addEventListener("pointerlockchange", this._onPointerLockChange, false);
document.addEventListener("webkitpointerlockchange", this._onPointerLockChange, false);
}

this.enableOfflineSupport = Engine.OfflineProviderFactory !== undefined;

this._deterministicLockstep = !!this._creationOptions.deterministicLockstep;
this._lockstepMaxSteps = this._creationOptions.lockstepMaxSteps || 0;
this._timeStep = this._creationOptions.timeStep || 1 / 60;
}

/**
Expand Down
197 changes: 98 additions & 99 deletions packages/dev/core/src/Engines/thinEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,23 +89,13 @@ export interface HostInformation {
isMobile: boolean;
}

/** Interface defining initialization parameters for Engine class */
export interface EngineOptions extends WebGLContextAttributes {
/** Interface defining initialization parameters for ThinEngine class */
export interface ThinEngineOptions {
/**
* Defines if the engine should no exceed a specified device ratio
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
*/
limitDeviceRatio?: number;
/**
* Defines if webvr should be enabled automatically
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/webVRCamera
*/
autoEnableWebVR?: boolean;
/**
* Defines if webgl2 should be turned off even if supported
* @see https://doc.babylonjs.com/setup/support/webGL2
*/
disableWebGL2Support?: boolean;
/**
* Defines if webaudio should be initialized as well
* @see https://doc.babylonjs.com/features/featuresDeepDive/audio/playingSoundsMusic
Expand Down Expand Up @@ -135,41 +125,72 @@ export interface EngineOptions extends WebGLContextAttributes {
* If not handle, you might need to set it up on your side for expected touch devices behavior.
*/
doNotHandleTouchAction?: boolean;

/**
* Defines that engine should compile shaders with high precision floats (if supported). True by default
* Make the matrix computations to be performed in 64 bits instead of 32 bits. False by default
*/
useHighPrecisionFloats?: boolean;
useHighPrecisionMatrix?: boolean;

/**
* Make the canvas XR Compatible for XR sessions
* Defines whether to adapt to the device's viewport characteristics (default: false)
*/
xrCompatible?: boolean;
adaptToDeviceRatio?: boolean;

/**
* Make the matrix computations to be performed in 64 bits instead of 32 bits. False by default
* True if the more expensive but exact conversions should be used for transforming colors to and from linear space within shaders.
* Otherwise, the default is to use a cheaper approximation.
*/
useHighPrecisionMatrix?: boolean;
useExactSrgbConversions?: boolean;

/**
* Will prevent the system from falling back to software implementation if a hardware device cannot be created
* Defines whether MSAA is enabled on the canvas.
*/
failIfMajorPerformanceCaveat?: boolean;
antialias?: boolean;

/**
* Defines whether to adapt to the device's viewport characteristics (default: false)
* Defines whether the stencil buffer should be enabled.
*/
adaptToDeviceRatio?: boolean;
stencil?: boolean;

/**
* Defines whether the canvas should be created in "premultiplied" mode (if false, the canvas is created in the "opaque" mode) (true by default)
*/
premultipliedAlpha?: boolean;
}

/** Interface defining initialization parameters for Engine class */
export interface EngineOptions extends ThinEngineOptions, WebGLContextAttributes {
/**
* Defines if webvr should be enabled automatically
* @see https://doc.babylonjs.com/features/featuresDeepDive/cameras/webVRCamera
*/
autoEnableWebVR?: boolean;
/**
* Defines if webgl2 should be turned off even if supported
* @see https://doc.babylonjs.com/setup/support/webGL2
*/
disableWebGL2Support?: boolean;

/**
* Defines that engine should compile shaders with high precision floats (if supported). True by default
*/
useHighPrecisionFloats?: boolean;
/**
* Make the canvas XR Compatible for XR sessions
*/
xrCompatible?: boolean;

/**
* Will prevent the system from falling back to software implementation if a hardware device cannot be created
*/
failIfMajorPerformanceCaveat?: boolean;

/**
* If sRGB Buffer support is not set during construction, use this value to force a specific state
* This is added due to an issue when processing textures in chrome/edge/firefox
* This will not influence NativeEngine and WebGPUEngine which set the behavior to true during construction.
*/
forceSRGBBufferSupportState?: boolean;
/**
* True if the more expensive but exact conversions should be used for transforming colors to and from linear space within shaders.
* Otherwise, the default is to use a cheaper approximation.
*/
useExactSrgbConversions?: boolean;
}

/**
Expand Down Expand Up @@ -326,12 +347,12 @@ export class ThinEngine {
/**
* Indicates if the z range in NDC space is 0..1 (value: true) or -1..1 (value: false)
*/
public readonly isNDCHalfZRange = false;
public readonly isNDCHalfZRange: boolean = false;

/**
* Indicates that the origin of the texture/framebuffer space is the bottom left corner. If false, the origin is top left
*/
public readonly hasOriginBottomLeft = true;
public readonly hasOriginBottomLeft: boolean = true;

/**
* Gets or sets a boolean indicating that uniform buffers must be disabled even if they are supported
Expand Down Expand Up @@ -780,84 +801,48 @@ export class ThinEngine {

PerformanceConfigurator.SetMatrixPrecision(!!options.useHighPrecisionMatrix);

options.antialias = antialias ?? options.antialias;
options.deterministicLockstep = options.deterministicLockstep ?? false;
options.lockstepMaxSteps = options.lockstepMaxSteps ?? 4;
options.timeStep = options.timeStep ?? 1 / 60;
options.audioEngine = options.audioEngine ?? true;
options.stencil = options.stencil ?? true;

this._audioContext = options.audioEngineOptions?.audioContext ?? null;
this._audioDestination = options.audioEngineOptions?.audioDestination ?? null;
this.premultipliedAlpha = options.premultipliedAlpha ?? true;
this._useExactSrgbConversions = options.useExactSrgbConversions ?? false;
this._doNotHandleContextLost = !!options.doNotHandleContextLost;
this._isStencilEnable = options.stencil ? true : false;

// Viewport
adaptToDeviceRatio = adaptToDeviceRatio || options.adaptToDeviceRatio || false;

const devicePixelRatio = IsWindowObjectExist() ? window.devicePixelRatio || 1.0 : 1.0;

const limitDeviceRatio = options.limitDeviceRatio || devicePixelRatio;
this._hardwareScalingLevel = adaptToDeviceRatio ? 1.0 / Math.min(limitDeviceRatio, devicePixelRatio) : 1.0;
this._lastDevicePixelRatio = devicePixelRatio;

if (!canvasOrContext) {
return;
}

adaptToDeviceRatio = adaptToDeviceRatio || options.adaptToDeviceRatio || false;

if ((canvasOrContext as any).getContext) {
canvas = <HTMLCanvasElement>canvasOrContext;
this._renderingCanvas = canvas;

if (antialias !== undefined) {
options.antialias = antialias;
}

if (options.deterministicLockstep === undefined) {
options.deterministicLockstep = false;
}

if (options.lockstepMaxSteps === undefined) {
options.lockstepMaxSteps = 4;
}

if (options.timeStep === undefined) {
options.timeStep = 1 / 60;
}

if (options.preserveDrawingBuffer === undefined) {
options.preserveDrawingBuffer = false;
}

if (options.audioEngine === undefined) {
options.audioEngine = true;
}

if (options.audioEngineOptions !== undefined && options.audioEngineOptions.audioContext !== undefined) {
this._audioContext = options.audioEngineOptions.audioContext;
}

if (options.audioEngineOptions !== undefined && options.audioEngineOptions.audioDestination !== undefined) {
this._audioDestination = options.audioEngineOptions.audioDestination;
}

if (options.stencil === undefined) {
options.stencil = true;
}

if (options.premultipliedAlpha === false) {
this.premultipliedAlpha = false;
}

if (options.xrCompatible === undefined) {
options.xrCompatible = true;
}

if (options.useExactSrgbConversions !== undefined) {
this._useExactSrgbConversions = options.useExactSrgbConversions;
}

this._doNotHandleContextLost = options.doNotHandleContextLost ? true : false;

// Exceptions
if (navigator && navigator.userAgent) {
// Function to check if running on mobile device
this._checkForMobile = () => {
const currentUA = navigator.userAgent;
this.hostInformation.isMobile =
currentUA.indexOf("Mobile") !== -1 ||
// Needed for iOS 13+ detection on iPad (inspired by solution from https://stackoverflow.com/questions/9038625/detect-if-device-is-ios)
(currentUA.indexOf("Mac") !== -1 && IsDocumentAvailable() && "ontouchend" in document);
};

// Set initial isMobile value
this._checkForMobile();

// Set up event listener to check when window is resized (used to get emulator activation to work properly)
if (IsWindowObjectExist()) {
window.addEventListener("resize", this._checkForMobile);
}
this._setupMobileChecks();

const ua = navigator.userAgent;
for (const exception of ThinEngine.ExceptionList) {
Expand Down Expand Up @@ -984,15 +969,8 @@ export class ThinEngine {
this._highPrecisionShadersAllowed = options.useHighPrecisionFloats;
}

// Viewport
const devicePixelRatio = IsWindowObjectExist() ? window.devicePixelRatio || 1.0 : 1.0;

const limitDeviceRatio = options.limitDeviceRatio || devicePixelRatio;
this._hardwareScalingLevel = adaptToDeviceRatio ? 1.0 / Math.min(limitDeviceRatio, devicePixelRatio) : 1.0;
this._lastDevicePixelRatio = devicePixelRatio;
this.resize();

this._isStencilEnable = options.stencil ? true : false;
this._initGLContext();
this._initFeatures();

Expand Down Expand Up @@ -1025,6 +1003,29 @@ export class ThinEngine {
}
}

protected _setupMobileChecks(): void {
if (!(navigator && navigator.userAgent)) {
return;
}

// Function to check if running on mobile device
this._checkForMobile = () => {
const currentUA = navigator.userAgent;
this.hostInformation.isMobile =
currentUA.indexOf("Mobile") !== -1 ||
// Needed for iOS 13+ detection on iPad (inspired by solution from https://stackoverflow.com/questions/9038625/detect-if-device-is-ios)
(currentUA.indexOf("Mac") !== -1 && IsDocumentAvailable() && "ontouchend" in document);
};

// Set initial isMobile value
this._checkForMobile();

// Set up event listener to check when window is resized (used to get emulator activation to work properly)
RaananW marked this conversation as resolved.
Show resolved Hide resolved
if (IsWindowObjectExist()) {
window.addEventListener("resize", this._checkForMobile);
}
}

protected _restoreEngineAfterContextLost(initEngine: () => void): void {
// Adding a timeout to avoid race condition at browser level
setTimeout(async () => {
Expand Down Expand Up @@ -1073,10 +1074,8 @@ export class ThinEngine {
/**
* Shared initialization across engines types.
* @param canvas The canvas associated with this instance of the engine.
* @param doNotHandleTouchAction Defines that engine should ignore modifying touch action attribute and style
* @param audioEngine Defines if an audio engine should be created by default
*/
protected _sharedInit(canvas: HTMLCanvasElement, doNotHandleTouchAction: boolean, audioEngine: boolean) {
protected _sharedInit(canvas: HTMLCanvasElement) {
this._renderingCanvas = canvas;
}

Expand Down
Loading