|
@@ -3,6 +3,7 @@ import { IInternalTextureLoader } from '../Materials/Textures/internalTextureLoa
|
|
|
import { Effect, IEffectCreationOptions } from '../Materials/effect';
|
|
|
import { _DevTools } from '../Misc/devTools';
|
|
|
import { IShaderProcessor } from './Processors/iShaderProcessor';
|
|
|
+import { ShaderProcessingContext } from "./Processors/shaderProcessingOptions";
|
|
|
import { UniformBuffer } from '../Materials/uniformBuffer';
|
|
|
import { Nullable, DataArray, IndicesArray } from '../types';
|
|
|
import { EngineCapabilities } from './engineCapabilities';
|
|
@@ -30,6 +31,9 @@ import { IEffectFallbacks } from '../Materials/iEffectFallbacks';
|
|
|
import { IWebRequest } from '../Misc/interfaces/iWebRequest';
|
|
|
import { CanvasGenerator } from '../Misc/canvasGenerator';
|
|
|
import { PerformanceConfigurator } from './performanceConfigurator';
|
|
|
+import { EngineFeatures } from './engineFeatures';
|
|
|
+import { HardwareTextureWrapper } from '../Materials/Textures/hardwareTextureWrapper';
|
|
|
+import { WebGLHardwareTexture } from './WebGL/webGLHardwareTexture';
|
|
|
|
|
|
declare type WebRequest = import("../Misc/webRequest").WebRequest;
|
|
|
declare type LoadFileError = import("../Misc/fileTools").LoadFileError;
|
|
@@ -172,7 +176,7 @@ export class ThinEngine {
|
|
|
* Returns a string describing the current engine
|
|
|
*/
|
|
|
public get description(): string {
|
|
|
- let description = "WebGL" + this.webGLVersion;
|
|
|
+ let description = this.name + this.webGLVersion;
|
|
|
|
|
|
if (this._caps.parallelShaderCompile) {
|
|
|
description += " - Parallel shader compilation";
|
|
@@ -181,6 +185,20 @@ export class ThinEngine {
|
|
|
return description;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns the name of the engine
|
|
|
+ */
|
|
|
+ public get name(): string {
|
|
|
+ return "WebGL";
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Returns the version of the engine
|
|
|
+ */
|
|
|
+ public get version(): number {
|
|
|
+ return this._webGLVersion;
|
|
|
+ }
|
|
|
+
|
|
|
// Updatable statics so stick with vars here
|
|
|
|
|
|
/**
|
|
@@ -201,7 +219,7 @@ export class ThinEngine {
|
|
|
// Public members
|
|
|
|
|
|
/** @hidden */
|
|
|
- public _shaderProcessor: IShaderProcessor;
|
|
|
+ public _shaderProcessor: Nullable<IShaderProcessor>;
|
|
|
|
|
|
/**
|
|
|
* Gets or sets a boolean that indicates if textures must be forced to power of 2 size even if not required
|
|
@@ -243,6 +261,14 @@ export class ThinEngine {
|
|
|
*/
|
|
|
public disableUniformBuffers = false;
|
|
|
|
|
|
+ private _frameId = 0;
|
|
|
+ /**
|
|
|
+ * Gets the current frame id
|
|
|
+ */
|
|
|
+ public get frameId(): number {
|
|
|
+ return this._frameId;
|
|
|
+ }
|
|
|
+
|
|
|
/** @hidden */
|
|
|
public _uniformBuffers = new Array<UniformBuffer>();
|
|
|
|
|
@@ -284,10 +310,12 @@ export class ThinEngine {
|
|
|
/** @hidden */
|
|
|
public _badDesktopOS = false;
|
|
|
|
|
|
- private _hardwareScalingLevel: number;
|
|
|
+ protected _hardwareScalingLevel: number;
|
|
|
/** @hidden */
|
|
|
public _caps: EngineCapabilities;
|
|
|
- private _isStencilEnable: boolean;
|
|
|
+ /** @hidden */
|
|
|
+ public _features: EngineFeatures;
|
|
|
+ protected _isStencilEnable: boolean;
|
|
|
|
|
|
private _glVersion: string;
|
|
|
private _glRenderer: string;
|
|
@@ -360,7 +388,7 @@ export class ThinEngine {
|
|
|
protected _currentEffect: Nullable<Effect>;
|
|
|
/** @hidden */
|
|
|
protected _currentProgram: Nullable<WebGLProgram>;
|
|
|
- private _compiledEffects: { [key: string]: Effect } = {};
|
|
|
+ protected _compiledEffects: { [key: string]: Effect } = {};
|
|
|
private _vertexAttribArraysEnabled: boolean[] = [];
|
|
|
/** @hidden */
|
|
|
protected _cachedViewport: Nullable<IViewportLike>;
|
|
@@ -489,13 +517,31 @@ export class ThinEngine {
|
|
|
/**
|
|
|
* Defines whether the engine has been created with the premultipliedAlpha option on or not.
|
|
|
*/
|
|
|
- public readonly premultipliedAlpha: boolean = true;
|
|
|
+ public premultipliedAlpha: boolean = true;
|
|
|
|
|
|
/**
|
|
|
* Observable event triggered before each texture is initialized
|
|
|
*/
|
|
|
public onBeforeTextureInitObservable = new Observable<Texture>();
|
|
|
|
|
|
+ /** @hidden */
|
|
|
+ protected _isWebGPU: boolean = false;
|
|
|
+ /**
|
|
|
+ * Gets a boolean indicating if the engine runs in WebGPU or not.
|
|
|
+ */
|
|
|
+ public get isWebGPU(): boolean {
|
|
|
+ return this._isWebGPU;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @hidden */
|
|
|
+ protected _shaderPlatformName: string;
|
|
|
+ /**
|
|
|
+ * Gets the shader platfrom name used by the effects.
|
|
|
+ */
|
|
|
+ public get shaderPlatformName(): string {
|
|
|
+ return this._shaderPlatformName;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Creates a new engine
|
|
|
* @param canvasOrContext defines the canvas or WebGL context to use for rendering. If you provide a WebGL context, Babylon.js will not hook events on the canvas (like pointers, keyboards, etc...) so no event observables will be available. This is mostly used when Babylon.js is used as a plugin on a system which alreay used the WebGL context
|
|
@@ -507,14 +553,14 @@ export class ThinEngine {
|
|
|
|
|
|
let canvas: Nullable<HTMLCanvasElement> = null;
|
|
|
|
|
|
- if (!canvasOrContext) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
options = options || {};
|
|
|
|
|
|
PerformanceConfigurator.SetMatrixPrecision(!!options.useHighPrecisionMatrix);
|
|
|
|
|
|
+ if (!canvasOrContext) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
if ((canvasOrContext as any).getContext) {
|
|
|
canvas = <HTMLCanvasElement>canvasOrContext;
|
|
|
this._renderingCanvas = canvas;
|
|
@@ -639,10 +685,12 @@ export class ThinEngine {
|
|
|
this._gl = <any>(canvas.getContext("webgl2", options) || canvas.getContext("experimental-webgl2", options));
|
|
|
if (this._gl) {
|
|
|
this._webGLVersion = 2.0;
|
|
|
+ this._shaderPlatformName = "WEBGL2";
|
|
|
|
|
|
// Prevent weird browsers to lie (yeah that happens!)
|
|
|
if (!this._gl.deleteQuery) {
|
|
|
this._webGLVersion = 1.0;
|
|
|
+ this._shaderPlatformName = "WEBGL1";
|
|
|
}
|
|
|
}
|
|
|
} catch (e) {
|
|
@@ -670,6 +718,10 @@ export class ThinEngine {
|
|
|
|
|
|
if (this._gl.renderbufferStorageMultisample) {
|
|
|
this._webGLVersion = 2.0;
|
|
|
+ this._shaderPlatformName = "WEBGL2";
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ this._shaderPlatformName = "WEBGL1";
|
|
|
}
|
|
|
|
|
|
const attributes = this._gl.getContextAttributes();
|
|
@@ -694,6 +746,7 @@ export class ThinEngine {
|
|
|
|
|
|
this._isStencilEnable = options.stencil ? true : false;
|
|
|
this._initGLContext();
|
|
|
+ this._initFeatures();
|
|
|
|
|
|
// Prepare buffer pointers
|
|
|
for (var i = 0; i < this._caps.maxVertexAttribs; i++) {
|
|
@@ -726,6 +779,53 @@ export class ThinEngine {
|
|
|
console.log(`Babylon.js v${ThinEngine.Version} - ${this.description}`);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * @hidden
|
|
|
+ */
|
|
|
+ public _debugPushGroup(groupName: string, targetObject?: number): void {
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @hidden
|
|
|
+ */
|
|
|
+ public _debugPopGroup(targetObject?: number): void {
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @hidden
|
|
|
+ */
|
|
|
+ public _debugInsertMarker(text: string, targetObject?: number): void {
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 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) {
|
|
|
+ this._renderingCanvas = canvas;
|
|
|
+
|
|
|
+ // Shader processor
|
|
|
+ this._shaderProcessor = this._getShaderProcessor();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Gets a shader processor implementation fitting with the current engine type.
|
|
|
+ * @returns The shader processor implementation.
|
|
|
+ */
|
|
|
+ protected _getShaderProcessor(): Nullable<IShaderProcessor> {
|
|
|
+ if (this.webGLVersion > 1) {
|
|
|
+ return new WebGL2ShaderProcessor();
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @hidden */
|
|
|
+ public _getShaderProcessingContext(): Nullable<ShaderProcessingContext> {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
private _rebuildInternalTextures(): void {
|
|
|
let currentState = this._internalTexturesCache.slice(); // Do a copy because the rebuild will add proxies
|
|
|
|
|
@@ -961,8 +1061,30 @@ export class ThinEngine {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ protected _initFeatures(): void {
|
|
|
+ this._features = {
|
|
|
+ forceBitmapOverHTMLImageElement: false,
|
|
|
+ supportRenderAndCopyToLodForFloatTextures: this._webGLVersion !== 1,
|
|
|
+ supportDepthStencilTexture: this._webGLVersion !== 1,
|
|
|
+ supportShadowSamplers: this._webGLVersion !== 1,
|
|
|
+ uniformBufferHardCheckMatrix: false,
|
|
|
+ allowTexturePrefiltering: this._webGLVersion !== 1,
|
|
|
+ trackUbosInFrame: false,
|
|
|
+ supportCSM: this._webGLVersion !== 1,
|
|
|
+ basisNeedsPOT: this._webGLVersion === 1,
|
|
|
+ support3DTextures: this._webGLVersion !== 1,
|
|
|
+ needTypeSuffixInShaderConstants: this._webGLVersion !== 1,
|
|
|
+ supportMSAA: this._webGLVersion !== 1,
|
|
|
+ supportSSAO2: this._webGLVersion !== 1,
|
|
|
+ supportExtendedTextureFormats: this._webGLVersion !== 1,
|
|
|
+ supportSwitchCaseInShader: this._webGLVersion !== 1,
|
|
|
+ _collectUbosUpdatedInFrame: false,
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Gets version of the current webGL context
|
|
|
+ * Keep it for back compat - use version instead
|
|
|
*/
|
|
|
public get webGLVersion(): number {
|
|
|
return this._webGLVersion;
|
|
@@ -1215,7 +1337,7 @@ export class ThinEngine {
|
|
|
this._gl.clear(mode);
|
|
|
}
|
|
|
|
|
|
- private _viewportCached = { x: 0, y: 0, z: 0, w: 0 };
|
|
|
+ protected _viewportCached = { x: 0, y: 0, z: 0, w: 0 };
|
|
|
|
|
|
/** @hidden */
|
|
|
public _viewport(x: number, y: number, width: number, height: number): void {
|
|
@@ -1263,12 +1385,14 @@ export class ThinEngine {
|
|
|
if (this._badOS) {
|
|
|
this.flushFramebuffer();
|
|
|
}
|
|
|
+ this._frameId++;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Resize the view according to the canvas' size
|
|
|
+ * @param forceSetSize true to force setting the sizes of the underlying canvas
|
|
|
*/
|
|
|
- public resize(): void {
|
|
|
+ public resize(forceSetSize = false): void {
|
|
|
let width: number;
|
|
|
let height: number;
|
|
|
|
|
@@ -1280,16 +1404,17 @@ export class ThinEngine {
|
|
|
height = this._renderingCanvas ? this._renderingCanvas.height : 100;
|
|
|
}
|
|
|
|
|
|
- this.setSize(width / this._hardwareScalingLevel, height / this._hardwareScalingLevel);
|
|
|
+ this.setSize(width / this._hardwareScalingLevel, height / this._hardwareScalingLevel, forceSetSize);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Force a specific size of the canvas
|
|
|
* @param width defines the new canvas' width
|
|
|
* @param height defines the new canvas' height
|
|
|
+ * @param forceSetSize true to force setting the sizes of the underlying canvas
|
|
|
* @returns true if the size was changed
|
|
|
*/
|
|
|
- public setSize(width: number, height: number): boolean {
|
|
|
+ public setSize(width: number, height: number, forceSetSize = false): boolean {
|
|
|
if (!this._renderingCanvas) {
|
|
|
return false;
|
|
|
}
|
|
@@ -1297,7 +1422,7 @@ export class ThinEngine {
|
|
|
width = width | 0;
|
|
|
height = height | 0;
|
|
|
|
|
|
- if (this._renderingCanvas.width === width && this._renderingCanvas.height === height) {
|
|
|
+ if (!forceSetSize && this._renderingCanvas.width === width && this._renderingCanvas.height === height) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -1326,23 +1451,23 @@ export class ThinEngine {
|
|
|
|
|
|
const gl = this._gl;
|
|
|
if (texture.is2DArray) {
|
|
|
- gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture._webGLTexture, lodLevel, layer);
|
|
|
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, texture._hardwareTexture!.underlyingResource, lodLevel, layer);
|
|
|
}
|
|
|
else if (texture.isCube) {
|
|
|
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, texture._webGLTexture, lodLevel);
|
|
|
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, texture._hardwareTexture!.underlyingResource, lodLevel);
|
|
|
}
|
|
|
|
|
|
const depthStencilTexture = texture._depthStencilTexture;
|
|
|
if (depthStencilTexture) {
|
|
|
const attachment = (depthStencilTexture._generateStencilBuffer) ? gl.DEPTH_STENCIL_ATTACHMENT : gl.DEPTH_ATTACHMENT;
|
|
|
if (texture.is2DArray) {
|
|
|
- gl.framebufferTextureLayer(gl.FRAMEBUFFER, attachment, depthStencilTexture._webGLTexture, lodLevel, layer);
|
|
|
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, attachment, depthStencilTexture._hardwareTexture!.underlyingResource, lodLevel, layer);
|
|
|
}
|
|
|
else if (texture.isCube) {
|
|
|
- gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, depthStencilTexture._webGLTexture, lodLevel);
|
|
|
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, depthStencilTexture._hardwareTexture!.underlyingResource, lodLevel);
|
|
|
}
|
|
|
else {
|
|
|
- gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, depthStencilTexture._webGLTexture, lodLevel);
|
|
|
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, attachment, gl.TEXTURE_2D, depthStencilTexture._hardwareTexture!.underlyingResource, lodLevel);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2098,8 +2223,7 @@ export class ThinEngine {
|
|
|
|
|
|
return compiledEffect;
|
|
|
}
|
|
|
- var effect = new Effect(baseName, attributesNamesOrOptions, uniformsNamesOrEngine, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters);
|
|
|
- effect._key = name;
|
|
|
+ var effect = new Effect(baseName, attributesNamesOrOptions, uniformsNamesOrEngine, samplers, this, defines, fallbacks, onCompiled, onError, indexParameters, name);
|
|
|
this._compiledEffects[name] = effect;
|
|
|
|
|
|
return effect;
|
|
@@ -2172,9 +2296,10 @@ export class ThinEngine {
|
|
|
|
|
|
/**
|
|
|
* Creates a new pipeline context
|
|
|
+ * @param shaderProcessingContext defines the shader processing context used during the processing if available
|
|
|
* @returns the new pipeline
|
|
|
*/
|
|
|
- public createPipelineContext(): IPipelineContext {
|
|
|
+ public createPipelineContext(shaderProcessingContext: Nullable<ShaderProcessingContext>): IPipelineContext {
|
|
|
var pipelineContext = new WebGLPipelineContext();
|
|
|
pipelineContext.engine = this;
|
|
|
|
|
@@ -2268,10 +2393,11 @@ export class ThinEngine {
|
|
|
}
|
|
|
|
|
|
/** @hidden */
|
|
|
- public _preparePipelineContext(pipelineContext: IPipelineContext, vertexSourceCode: string, fragmentSourceCode: string, createAsRaw: boolean,
|
|
|
+ public _preparePipelineContext(pipelineContext: IPipelineContext, vertexSourceCode: string, fragmentSourceCode: string, createAsRaw: boolean, rawVertexSourceCode: string, rawFragmentSourceCode: string,
|
|
|
rebuildRebind: any,
|
|
|
defines: Nullable<string>,
|
|
|
- transformFeedbackVaryings: Nullable<string[]>) {
|
|
|
+ transformFeedbackVaryings: Nullable<string[]>,
|
|
|
+ key: string) {
|
|
|
let webGLRenderingState = pipelineContext as WebGLPipelineContext;
|
|
|
|
|
|
if (createAsRaw) {
|
|
@@ -2333,7 +2459,7 @@ export class ThinEngine {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Gets the lsit of active attributes for a given webGL program
|
|
|
+ * Gets the list of active attributes for a given webGL program
|
|
|
* @param pipelineContext defines the pipeline context to use
|
|
|
* @param attributesNames defines the list of attribute names to get
|
|
|
* @returns an array of indices indicating the offset of each attribute
|
|
@@ -2392,6 +2518,60 @@ export class ThinEngine {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * Set the value of an uniform to a int2
|
|
|
+ * @param uniform defines the webGL uniform location where to store the value
|
|
|
+ * @param x defines the 1st component of the value
|
|
|
+ * @param y defines the 2nd component of the value
|
|
|
+ * @returns true if the value was set
|
|
|
+ */
|
|
|
+ public setInt2(uniform: Nullable<WebGLUniformLocation>, x: number, y: number): boolean {
|
|
|
+ if (!uniform) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this._gl.uniform2i(uniform, x, y);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set the value of an uniform to a int3
|
|
|
+ * @param uniform defines the webGL uniform location where to store the value
|
|
|
+ * @param x defines the 1st component of the value
|
|
|
+ * @param y defines the 2nd component of the value
|
|
|
+ * @param z defines the 3rd component of the value
|
|
|
+ * @returns true if the value was set
|
|
|
+ */
|
|
|
+ public setInt3(uniform: Nullable<WebGLUniformLocation>, x: number, y: number, z: number): boolean {
|
|
|
+ if (!uniform) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this._gl.uniform3i(uniform, x, y, z);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Set the value of an uniform to a int4
|
|
|
+ * @param uniform defines the webGL uniform location where to store the value
|
|
|
+ * @param x defines the 1st component of the value
|
|
|
+ * @param y defines the 2nd component of the value
|
|
|
+ * @param z defines the 3rd component of the value
|
|
|
+ * @param w defines the 4th component of the value
|
|
|
+ * @returns true if the value was set
|
|
|
+ */
|
|
|
+ public setInt4(uniform: Nullable<WebGLUniformLocation>, x: number, y: number, z: number, w: number): boolean {
|
|
|
+ if (!uniform) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this._gl.uniform4i(uniform, x, y, z, w);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* Set the value of an uniform to an array of int32
|
|
|
* @param uniform defines the webGL uniform location where to store the value
|
|
|
* @param array defines the array of int32 to store
|
|
@@ -2839,7 +3019,7 @@ export class ThinEngine {
|
|
|
}
|
|
|
|
|
|
/** @hidden */
|
|
|
- public _createTexture(): WebGLTexture {
|
|
|
+ protected _createTexture(): WebGLTexture {
|
|
|
let texture = this._gl.createTexture();
|
|
|
|
|
|
if (!texture) {
|
|
@@ -2849,29 +3029,16 @@ export class ThinEngine {
|
|
|
return texture;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Usually called from Texture.ts.
|
|
|
- * Passed information to create a WebGLTexture
|
|
|
- * @param url defines a value which contains one of the following:
|
|
|
- * * A conventional http URL, e.g. 'http://...' or 'file://...'
|
|
|
- * * A base64 string of in-line texture data, e.g. 'data:image/jpg;base64,/...'
|
|
|
- * * An indicator that data being passed using the buffer parameter, e.g. 'data:mytexture.jpg'
|
|
|
- * @param noMipmap defines a boolean indicating that no mipmaps shall be generated. Ignored for compressed textures. They must be in the file
|
|
|
- * @param invertY when true, image is flipped when loaded. You probably want true. Certain compressed textures may invert this if their default is inverted (eg. ktx)
|
|
|
- * @param scene needed for loading to the correct scene
|
|
|
- * @param samplingMode mode with should be used sample / access the texture (Default: Texture.TRILINEAR_SAMPLINGMODE)
|
|
|
- * @param onLoad optional callback to be called upon successful completion
|
|
|
- * @param onError optional callback to be called upon failure
|
|
|
- * @param buffer a source of a file previously fetched as either a base64 string, an ArrayBuffer (compressed or image format), HTMLImageElement (image format), or a Blob
|
|
|
- * @param fallback an internal argument in case the function must be called again, due to etc1 not having alpha capabilities
|
|
|
- * @param format internal format. Default: RGB when extension is '.jpg' else RGBA. Ignored for compressed textures
|
|
|
- * @param forcedExtension defines the extension to use to pick the right loader
|
|
|
- * @param mimeType defines an optional mime type
|
|
|
- * @param loaderOptions options to be passed to the loader
|
|
|
- * @returns a InternalTexture for assignment back into BABYLON.Texture
|
|
|
- */
|
|
|
- public createTexture(url: Nullable<string>, noMipmap: boolean, invertY: boolean, scene: Nullable<ISceneLike>, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE,
|
|
|
+ /** @hidden */
|
|
|
+ public _createHardwareTexture(): HardwareTextureWrapper {
|
|
|
+ return new WebGLHardwareTexture(this._createTexture(), this._gl);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected _createTextureBase(url: Nullable<string>, noMipmap: boolean, invertY: boolean, scene: Nullable<ISceneLike>, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE,
|
|
|
onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null,
|
|
|
+ prepareTexture: (texture: InternalTexture, extension: string, scene: Nullable<ISceneLike>, img: HTMLImageElement | ImageBitmap | { width: number, height: number }, invertY: boolean, noMipmap: boolean, isCompressed: boolean,
|
|
|
+ processFunction: (width: number, height: number, img: HTMLImageElement | ImageBitmap | { width: number, height: number }, extension: string, texture: InternalTexture, continuationCallback: () => void) => boolean, samplingMode: number) => void,
|
|
|
+ prepareTextureProcessFunction: (width: number, height: number, img: HTMLImageElement | ImageBitmap | { width: number, height: number }, extension: string, texture: InternalTexture, continuationCallback: () => void) => boolean,
|
|
|
buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob | ImageBitmap> = null, fallback: Nullable<InternalTexture> = null, format: Nullable<number> = null,
|
|
|
forcedExtension: Nullable<string> = null, mimeType?: string, loaderOptions?: any): InternalTexture {
|
|
|
url = url || "";
|
|
@@ -2940,7 +3107,7 @@ export class ThinEngine {
|
|
|
}
|
|
|
|
|
|
if (EngineStore.UseFallbackTexture) {
|
|
|
- this.createTexture(EngineStore.FallbackTexture, noMipmap, texture.invertY, scene, samplingMode, null, onError, buffer, texture);
|
|
|
+ this._createTextureBase(EngineStore.FallbackTexture, noMipmap, texture.invertY, scene, samplingMode, null, onError, prepareTexture, prepareTextureProcessFunction, buffer, texture);
|
|
|
}
|
|
|
|
|
|
if (onError) {
|
|
@@ -2950,7 +3117,7 @@ export class ThinEngine {
|
|
|
else {
|
|
|
// fall back to the original url if the transformed url fails to load
|
|
|
Logger.Warn(`Failed to load ${url}, falling back to ${originalUrl}`);
|
|
|
- this.createTexture(originalUrl, noMipmap, texture.invertY, scene, samplingMode, onLoad, onError, buffer, texture, format, forcedExtension, mimeType, loaderOptions);
|
|
|
+ this._createTextureBase(originalUrl, noMipmap, texture.invertY, scene, samplingMode, onLoad, onError, prepareTexture, prepareTextureProcessFunction, buffer, texture, format, forcedExtension, mimeType, loaderOptions);
|
|
|
}
|
|
|
};
|
|
|
|
|
@@ -2961,7 +3128,7 @@ export class ThinEngine {
|
|
|
if (loadFailed) {
|
|
|
onInternalError("TextureLoader failed to load data");
|
|
|
} else {
|
|
|
- this._prepareWebGLTexture(texture, scene, width, height, texture.invertY, !loadMipmap, isCompressed, () => {
|
|
|
+ prepareTexture(texture, extension, scene, { width, height }, texture.invertY, !loadMipmap, isCompressed, () => {
|
|
|
done();
|
|
|
return false;
|
|
|
}, samplingMode);
|
|
@@ -2994,50 +3161,7 @@ export class ThinEngine {
|
|
|
texture._buffer = img;
|
|
|
}
|
|
|
|
|
|
- this._prepareWebGLTexture(texture, scene, img.width, img.height, texture.invertY, noMipmap, false, (potWidth, potHeight, continuationCallback) => {
|
|
|
- let gl = this._gl;
|
|
|
- var isPot = (img.width === potWidth && img.height === potHeight);
|
|
|
- let internalFormat = format ? this._getInternalFormat(format) : ((extension === ".jpg") ? gl.RGB : gl.RGBA);
|
|
|
-
|
|
|
- if (isPot) {
|
|
|
- gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, img);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- let maxTextureSize = this._caps.maxTextureSize;
|
|
|
-
|
|
|
- if (img.width > maxTextureSize || img.height > maxTextureSize || !this._supportsHardwareTextureRescaling) {
|
|
|
- this._prepareWorkingCanvas();
|
|
|
- if (!this._workingCanvas || !this._workingContext) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- this._workingCanvas.width = potWidth;
|
|
|
- this._workingCanvas.height = potHeight;
|
|
|
-
|
|
|
- this._workingContext.drawImage(img, 0, 0, img.width, img.height, 0, 0, potWidth, potHeight);
|
|
|
- gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, this._workingCanvas);
|
|
|
-
|
|
|
- texture.width = potWidth;
|
|
|
- texture.height = potHeight;
|
|
|
-
|
|
|
- return false;
|
|
|
- } else {
|
|
|
- // Using shaders when possible to rescale because canvas.drawImage is lossy
|
|
|
- let source = new InternalTexture(this, InternalTextureSource.Temp);
|
|
|
- this._bindTextureDirectly(gl.TEXTURE_2D, source, true);
|
|
|
- gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, img);
|
|
|
-
|
|
|
- this._rescaleTexture(source, texture, scene, internalFormat, () => {
|
|
|
- this._releaseTexture(source);
|
|
|
- this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
|
|
|
-
|
|
|
- continuationCallback();
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }, samplingMode);
|
|
|
+ prepareTexture(texture, extension, scene, img, texture.invertY, noMipmap, false, prepareTextureProcessFunction, samplingMode);
|
|
|
};
|
|
|
|
|
|
if (!fromData || isBase64) {
|
|
@@ -3059,6 +3183,83 @@ export class ThinEngine {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * Usually called from Texture.ts.
|
|
|
+ * Passed information to create a WebGLTexture
|
|
|
+ * @param url defines a value which contains one of the following:
|
|
|
+ * * A conventional http URL, e.g. 'http://...' or 'file://...'
|
|
|
+ * * A base64 string of in-line texture data, e.g. 'data:image/jpg;base64,/...'
|
|
|
+ * * An indicator that data being passed using the buffer parameter, e.g. 'data:mytexture.jpg'
|
|
|
+ * @param noMipmap defines a boolean indicating that no mipmaps shall be generated. Ignored for compressed textures. They must be in the file
|
|
|
+ * @param invertY when true, image is flipped when loaded. You probably want true. Certain compressed textures may invert this if their default is inverted (eg. ktx)
|
|
|
+ * @param scene needed for loading to the correct scene
|
|
|
+ * @param samplingMode mode with should be used sample / access the texture (Default: Texture.TRILINEAR_SAMPLINGMODE)
|
|
|
+ * @param onLoad optional callback to be called upon successful completion
|
|
|
+ * @param onError optional callback to be called upon failure
|
|
|
+ * @param buffer a source of a file previously fetched as either a base64 string, an ArrayBuffer (compressed or image format), HTMLImageElement (image format), or a Blob
|
|
|
+ * @param fallback an internal argument in case the function must be called again, due to etc1 not having alpha capabilities
|
|
|
+ * @param format internal format. Default: RGB when extension is '.jpg' else RGBA. Ignored for compressed textures
|
|
|
+ * @param forcedExtension defines the extension to use to pick the right loader
|
|
|
+ * @param mimeType defines an optional mime type
|
|
|
+ * @param loaderOptions options to be passed to the loader
|
|
|
+ * @returns a InternalTexture for assignment back into BABYLON.Texture
|
|
|
+ */
|
|
|
+ public createTexture(url: Nullable<string>, noMipmap: boolean, invertY: boolean, scene: Nullable<ISceneLike>, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE,
|
|
|
+ onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null,
|
|
|
+ buffer: Nullable<string | ArrayBuffer | ArrayBufferView | HTMLImageElement | Blob | ImageBitmap> = null, fallback: Nullable<InternalTexture> = null, format: Nullable<number> = null,
|
|
|
+ forcedExtension: Nullable<string> = null, mimeType?: string, loaderOptions?: any): InternalTexture {
|
|
|
+
|
|
|
+ return this._createTextureBase(
|
|
|
+ url, noMipmap, invertY, scene, samplingMode, onLoad, onError,
|
|
|
+ this._prepareWebGLTexture.bind(this),
|
|
|
+ (potWidth, potHeight, img, extension, texture, continuationCallback) => {
|
|
|
+ let gl = this._gl;
|
|
|
+ var isPot = (img.width === potWidth && img.height === potHeight);
|
|
|
+ let internalFormat = format ? this._getInternalFormat(format) : ((extension === ".jpg") ? gl.RGB : gl.RGBA);
|
|
|
+
|
|
|
+ if (isPot) {
|
|
|
+ gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, img as any);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ let maxTextureSize = this._caps.maxTextureSize;
|
|
|
+
|
|
|
+ if (img.width > maxTextureSize || img.height > maxTextureSize || !this._supportsHardwareTextureRescaling) {
|
|
|
+ this._prepareWorkingCanvas();
|
|
|
+ if (!this._workingCanvas || !this._workingContext) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ this._workingCanvas.width = potWidth;
|
|
|
+ this._workingCanvas.height = potHeight;
|
|
|
+
|
|
|
+ this._workingContext.drawImage(img as any, 0, 0, img.width, img.height, 0, 0, potWidth, potHeight);
|
|
|
+ gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, this._workingCanvas);
|
|
|
+
|
|
|
+ texture.width = potWidth;
|
|
|
+ texture.height = potHeight;
|
|
|
+
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ // Using shaders when possible to rescale because canvas.drawImage is lossy
|
|
|
+ let source = new InternalTexture(this, InternalTextureSource.Temp);
|
|
|
+ this._bindTextureDirectly(gl.TEXTURE_2D, source, true);
|
|
|
+ gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, internalFormat, gl.UNSIGNED_BYTE, img as any);
|
|
|
+
|
|
|
+ this._rescaleTexture(source, texture, scene, internalFormat, () => {
|
|
|
+ this._releaseTexture(source);
|
|
|
+ this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
|
|
|
+
|
|
|
+ continuationCallback();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ buffer, fallback, format, forcedExtension, mimeType, loaderOptions
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* Loads an image as an HTMLImageElement.
|
|
|
* @param input url string, ArrayBuffer, or Blob to load
|
|
|
* @param onLoad callback called when the image successfully loads
|
|
@@ -3209,6 +3410,16 @@ export class ThinEngine {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * Update the dimensions of a texture
|
|
|
+ * @param texture texture to update
|
|
|
+ * @param width new width of the texture
|
|
|
+ * @param height new height of the texture
|
|
|
+ * @param depth new depth of the texture
|
|
|
+ */
|
|
|
+ public updateTextureDimensions(texture: InternalTexture, width: number, height: number, depth: number = 1): void {
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* Update the sampling mode of a given texture
|
|
|
* @param texture defines the texture to update
|
|
|
* @param wrapU defines the texture wrap mode of the u coordinates
|
|
@@ -3235,7 +3446,7 @@ export class ThinEngine {
|
|
|
}
|
|
|
|
|
|
/** @hidden */
|
|
|
- public _setupDepthStencilTexture(internalTexture: InternalTexture, size: number | { width: number, height: number, layers?: number }, generateStencil: boolean, bilinearFiltering: boolean, comparisonFunction: number): void {
|
|
|
+ public _setupDepthStencilTexture(internalTexture: InternalTexture, size: number | { width: number, height: number, layers?: number }, generateStencil: boolean, bilinearFiltering: boolean, comparisonFunction: number, samples = 1): void {
|
|
|
const width = (<{ width: number, height: number, layers?: number }>size).width || <number>size;
|
|
|
const height = (<{ width: number, height: number, layers?: number }>size).height || <number>size;
|
|
|
const layers = (<{ width: number, height: number, layers?: number }>size).layers || 0;
|
|
@@ -3247,7 +3458,7 @@ export class ThinEngine {
|
|
|
internalTexture.is2DArray = layers > 0;
|
|
|
internalTexture.depth = layers;
|
|
|
internalTexture.isReady = true;
|
|
|
- internalTexture.samples = 1;
|
|
|
+ internalTexture.samples = samples;
|
|
|
internalTexture.generateMipMaps = false;
|
|
|
internalTexture._generateDepthBuffer = true;
|
|
|
internalTexture._generateStencilBuffer = generateStencil;
|
|
@@ -3373,18 +3584,18 @@ export class ThinEngine {
|
|
|
texture.onLoadedObservable.clear();
|
|
|
}
|
|
|
|
|
|
- private _prepareWebGLTexture(texture: InternalTexture, scene: Nullable<ISceneLike>, width: number, height: number, invertY: boolean, noMipmap: boolean, isCompressed: boolean,
|
|
|
- processFunction: (width: number, height: number, continuationCallback: () => void) => boolean, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): void {
|
|
|
+ private _prepareWebGLTexture(texture: InternalTexture, extension: string, scene: Nullable<ISceneLike>, img: HTMLImageElement | ImageBitmap | { width: number, height: number }, invertY: boolean, noMipmap: boolean, isCompressed: boolean,
|
|
|
+ processFunction: (width: number, height: number, img: HTMLImageElement | ImageBitmap | { width: number, height: number }, extension: string, texture: InternalTexture, continuationCallback: () => void) => boolean, samplingMode: number = Constants.TEXTURE_TRILINEAR_SAMPLINGMODE): void {
|
|
|
var maxTextureSize = this.getCaps().maxTextureSize;
|
|
|
- var potWidth = Math.min(maxTextureSize, this.needPOTTextures ? ThinEngine.GetExponentOfTwo(width, maxTextureSize) : width);
|
|
|
- var potHeight = Math.min(maxTextureSize, this.needPOTTextures ? ThinEngine.GetExponentOfTwo(height, maxTextureSize) : height);
|
|
|
+ var potWidth = Math.min(maxTextureSize, this.needPOTTextures ? ThinEngine.GetExponentOfTwo(img.width, maxTextureSize) : img.width);
|
|
|
+ var potHeight = Math.min(maxTextureSize, this.needPOTTextures ? ThinEngine.GetExponentOfTwo(img.height, maxTextureSize) : img.height);
|
|
|
|
|
|
var gl = this._gl;
|
|
|
if (!gl) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (!texture._webGLTexture) {
|
|
|
+ if (!texture._hardwareTexture!.underlyingResource) {
|
|
|
// this.resetTextureCache();
|
|
|
if (scene) {
|
|
|
scene._removePendingData(texture);
|
|
@@ -3396,13 +3607,13 @@ export class ThinEngine {
|
|
|
this._bindTextureDirectly(gl.TEXTURE_2D, texture, true);
|
|
|
this._unpackFlipY(invertY === undefined ? true : (invertY ? true : false));
|
|
|
|
|
|
- texture.baseWidth = width;
|
|
|
- texture.baseHeight = height;
|
|
|
+ texture.baseWidth = img.width;
|
|
|
+ texture.baseHeight = img.height;
|
|
|
texture.width = potWidth;
|
|
|
texture.height = potHeight;
|
|
|
texture.isReady = true;
|
|
|
|
|
|
- if (processFunction(potWidth, potHeight, () => {
|
|
|
+ if (processFunction(potWidth, potHeight, img, extension, texture, () => {
|
|
|
this._prepareWebGLTextureContinuation(texture, scene, noMipmap, isCompressed, samplingMode);
|
|
|
})) {
|
|
|
// Returning as texture needs extra async steps
|
|
@@ -3483,7 +3694,7 @@ export class ThinEngine {
|
|
|
public _releaseTexture(texture: InternalTexture): void {
|
|
|
this._releaseFramebufferObjects(texture);
|
|
|
|
|
|
- this._deleteTexture(texture._webGLTexture);
|
|
|
+ this._deleteTexture(texture._hardwareTexture?.underlyingResource);
|
|
|
|
|
|
// Unbind channels
|
|
|
this.unbindAllTextures();
|
|
@@ -3508,10 +3719,14 @@ export class ThinEngine {
|
|
|
if (texture._irradianceTexture) {
|
|
|
texture._irradianceTexture.dispose();
|
|
|
}
|
|
|
+
|
|
|
+ texture._depthStencilTexture?.dispose();
|
|
|
}
|
|
|
|
|
|
protected _deleteTexture(texture: Nullable<WebGLTexture>): void {
|
|
|
- this._gl.deleteTexture(texture);
|
|
|
+ if (texture) {
|
|
|
+ this._gl.deleteTexture(texture);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
protected _setProgram(program: WebGLProgram): void {
|
|
@@ -3564,7 +3779,7 @@ export class ThinEngine {
|
|
|
if (texture && texture.isMultiview) {
|
|
|
this._gl.bindTexture(target, texture ? texture._colorTextureArray : null);
|
|
|
} else {
|
|
|
- this._gl.bindTexture(target, texture ? texture._webGLTexture : null);
|
|
|
+ this._gl.bindTexture(target, texture ? texture._hardwareTexture!.underlyingResource : null);
|
|
|
}
|
|
|
|
|
|
this._boundTexturesCache[this._activeChannel] = texture;
|
|
@@ -3585,7 +3800,7 @@ export class ThinEngine {
|
|
|
}
|
|
|
|
|
|
/** @hidden */
|
|
|
- public _bindTexture(channel: number, texture: Nullable<InternalTexture>): void {
|
|
|
+ public _bindTexture(channel: number, texture: Nullable<InternalTexture>, name: string): void {
|
|
|
if (channel === undefined) {
|
|
|
return;
|
|
|
}
|
|
@@ -3619,8 +3834,9 @@ export class ThinEngine {
|
|
|
* @param channel The texture channel
|
|
|
* @param uniform The uniform to set
|
|
|
* @param texture The texture to apply
|
|
|
+ * @param name The name of the uniform in the effect
|
|
|
*/
|
|
|
- public setTexture(channel: number, uniform: Nullable<WebGLUniformLocation>, texture: Nullable<ThinTexture>): void {
|
|
|
+ public setTexture(channel: number, uniform: Nullable<WebGLUniformLocation>, texture: Nullable<ThinTexture>, name: string): void {
|
|
|
if (channel === undefined) {
|
|
|
return;
|
|
|
}
|
|
@@ -3653,7 +3869,7 @@ export class ThinEngine {
|
|
|
return this._gl.REPEAT;
|
|
|
}
|
|
|
|
|
|
- protected _setTexture(channel: number, texture: Nullable<ThinTexture>, isPartOfTextureArray = false, depthStencilTexture = false): boolean {
|
|
|
+ protected _setTexture(channel: number, texture: Nullable<ThinTexture>, isPartOfTextureArray = false, depthStencilTexture = false, name = ""): boolean {
|
|
|
// Not ready?
|
|
|
if (!texture) {
|
|
|
if (this._boundTexturesCache[channel] != null) {
|
|
@@ -3752,8 +3968,9 @@ export class ThinEngine {
|
|
|
* @param channel defines the channel where the texture array must be set
|
|
|
* @param uniform defines the associated uniform location
|
|
|
* @param textures defines the array of textures to bind
|
|
|
+ * @param name name of the channel
|
|
|
*/
|
|
|
- public setTextureArray(channel: number, uniform: Nullable<WebGLUniformLocation>, textures: ThinTexture[]): void {
|
|
|
+ public setTextureArray(channel: number, uniform: Nullable<WebGLUniformLocation>, textures: ThinTexture[], name: string): void {
|
|
|
if (channel === undefined || !uniform) {
|
|
|
return;
|
|
|
}
|
|
@@ -4308,14 +4525,18 @@ export class ThinEngine {
|
|
|
* @param width defines the width of the rectangle where pixels must be read
|
|
|
* @param height defines the height of the rectangle where pixels must be read
|
|
|
* @param hasAlpha defines whether the output should have alpha or not (defaults to true)
|
|
|
- * @returns a Uint8Array containing RGBA colors
|
|
|
+ * @param flushRenderer true to flush the renderer from the pending commands before reading the pixels
|
|
|
+ * @returns a ArrayBufferView promise (Uint8Array) containing RGBA colors
|
|
|
*/
|
|
|
- public readPixels(x: number, y: number, width: number, height: number, hasAlpha = true): Uint8Array {
|
|
|
+ public readPixels(x: number, y: number, width: number, height: number, hasAlpha = true, flushRenderer = true): Promise<ArrayBufferView> {
|
|
|
const numChannels = hasAlpha ? 4 : 3;
|
|
|
const format = hasAlpha ? this._gl.RGBA : this._gl.RGB;
|
|
|
const data = new Uint8Array(height * width * numChannels);
|
|
|
+ if (flushRenderer) {
|
|
|
+ this.flushFramebuffer();
|
|
|
+ }
|
|
|
this._gl.readPixels(x, y, width, height, format, this._gl.UNSIGNED_BYTE, data);
|
|
|
- return data;
|
|
|
+ return Promise.resolve(data);
|
|
|
}
|
|
|
|
|
|
// Statics
|