@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
1,195 lines (1,193 loc) • 95.4 kB
JavaScript
import { Engine } from "../Engines/engine.js";
import { InternalTexture } from "../Materials/Textures/internalTexture.js";
import { Texture } from "../Materials/Textures/texture.js";
import { DataBuffer } from "../Buffers/dataBuffer.js";
import { Tools } from "../Misc/tools.js";
import { Observable } from "../Misc/observable.js";
import { CreateRadianceImageDataArrayBufferViews, GetEnvInfo, UploadEnvSpherical } from "../Misc/environmentTextureTools.js";
import { Logger } from "../Misc/logger.js";
import { ThinEngine } from "./thinEngine.js";
import { EngineStore } from "./engineStore.js";
import { ShaderCodeInliner } from "./Processors/shaderCodeInliner.js";
import { NativeShaderProcessor } from "./Native/nativeShaderProcessors.js";
import { NativeDataStream } from "./Native/nativeDataStream.js";
import { NativePipelineContext } from "./Native/nativePipelineContext.js";
import { NativeRenderTargetWrapper } from "./Native/nativeRenderTargetWrapper.js";
import { NativeHardwareTexture } from "./Native/nativeHardwareTexture.js";
import { getNativeAlphaMode, getNativeAttribType, getNativeSamplingMode, getNativeTextureFormat, getNativeStencilDepthFail, getNativeStencilDepthPass, getNativeStencilFunc, getNativeStencilOpFail, getNativeAddressMode, } from "./Native/nativeHelpers.js";
import { checkNonFloatVertexBuffers } from "../Buffers/buffer.nonFloatVertexBuffers.js";
import { NativeShaderProcessingContext } from "./Native/nativeShaderProcessingContext.js";
import "../Buffers/buffer.align.js";
import { _GetCompatibleTextureLoader } from "../Materials/Textures/Loaders/textureLoaderManager.js";
import { _TimeToken } from "../Instrumentation/timeToken.js";
const onNativeObjectInitialized = new Observable();
if (typeof self !== "undefined" && !Object.prototype.hasOwnProperty.call(self, "_native")) {
let __native;
Object.defineProperty(self, "_native", {
get: () => __native,
set: (value) => {
__native = value;
if (__native) {
onNativeObjectInitialized.notifyObservers(__native);
}
},
});
}
/**
* Returns _native only after it has been defined by BabylonNative.
* @internal
*/
export async function AcquireNativeObjectAsync() {
return await new Promise((resolve) => {
if (typeof _native === "undefined") {
onNativeObjectInitialized.addOnce((nativeObject) => resolve(nativeObject));
}
else {
resolve(_native);
}
});
}
/**
* Registers a constructor on the _native object. See NativeXRFrame for an example.
* @internal
*/
export async function RegisterNativeTypeAsync(typeName, constructor) {
(await AcquireNativeObjectAsync())[typeName] = constructor;
}
/**
* Container for accessors for natively-stored mesh data buffers.
*/
class NativeDataBuffer extends DataBuffer {
}
/** @internal */
class CommandBufferEncoder {
constructor(_engine) {
this._engine = _engine;
this._pending = new Array();
this._isCommandBufferScopeActive = false;
this._commandStream = NativeEngine._createNativeDataStream();
this._engine.setCommandDataStream(this._commandStream);
}
beginCommandScope() {
if (this._isCommandBufferScopeActive) {
throw new Error("Command scope already active.");
}
this._isCommandBufferScopeActive = true;
}
endCommandScope() {
if (!this._isCommandBufferScopeActive) {
throw new Error("Command scope is not active.");
}
this._isCommandBufferScopeActive = false;
this._submit();
}
startEncodingCommand(command) {
this._commandStream.writeNativeData(command);
}
encodeCommandArgAsUInt32(commandArg) {
this._commandStream.writeUint32(commandArg);
}
encodeCommandArgAsUInt32s(commandArg) {
this._commandStream.writeUint32Array(commandArg);
}
encodeCommandArgAsInt32(commandArg) {
this._commandStream.writeInt32(commandArg);
}
encodeCommandArgAsInt32s(commandArg) {
this._commandStream.writeInt32Array(commandArg);
}
encodeCommandArgAsFloat32(commandArg) {
this._commandStream.writeFloat32(commandArg);
}
encodeCommandArgAsFloat32s(commandArg) {
this._commandStream.writeFloat32Array(commandArg);
}
encodeCommandArgAsNativeData(commandArg) {
this._commandStream.writeNativeData(commandArg);
this._pending.push(commandArg);
}
finishEncodingCommand() {
if (!this._isCommandBufferScopeActive) {
this._submit();
}
}
_submit() {
this._engine.submitCommands();
this._pending.length = 0;
}
}
const remappedAttributesNames = [];
/** @internal */
export class NativeEngine extends Engine {
setHardwareScalingLevel(level) {
super.setHardwareScalingLevel(level);
this._engine.setHardwareScalingLevel(level);
}
constructor(options = {}) {
super(null, false, undefined, options.adaptToDeviceRatio);
this._engine = new _native.Engine({
version: Engine.Version,
nonFloatVertexBuffers: true,
});
this._camera = _native.Camera ? new _native.Camera() : null;
this._commandBufferEncoder = new CommandBufferEncoder(this._engine);
this._frameStats = { gpuTimeNs: Number.NaN };
this._boundBuffersVertexArray = null;
this._currentDepthTest = _native.Engine.DEPTH_TEST_LEQUAL;
this._stencilTest = false;
this._stencilMask = 255;
this._stencilFunc = 519;
this._stencilFuncRef = 0;
this._stencilFuncMask = 255;
this._stencilOpStencilFail = 7680;
this._stencilOpDepthFail = 7680;
this._stencilOpStencilDepthPass = 7681;
this._zOffset = 0;
this._zOffsetUnits = 0;
this._depthWrite = true;
// warning for non supported fill mode has already been displayed
this._fillModeWarningDisplayed = false;
if (_native.Engine.PROTOCOL_VERSION !== NativeEngine.PROTOCOL_VERSION) {
throw new Error(`Protocol version mismatch: ${_native.Engine.PROTOCOL_VERSION} (Native) !== ${NativeEngine.PROTOCOL_VERSION} (JS)`);
}
if (this._engine.setDeviceLostCallback) {
this._engine.setDeviceLostCallback(() => {
this.onContextLostObservable.notifyObservers(this);
this._contextWasLost = true;
this._restoreEngineAfterContextLost();
});
}
this._webGLVersion = 2;
this.disableUniformBuffers = true;
this._shaderPlatformName = "NATIVE";
// TODO: Initialize this more correctly based on the hardware capabilities.
// Init caps
this._caps = {
maxTexturesImageUnits: 16,
maxVertexTextureImageUnits: 16,
maxCombinedTexturesImageUnits: 32,
maxTextureSize: _native.Engine.CAPS_LIMITS_MAX_TEXTURE_SIZE,
maxCubemapTextureSize: 512,
maxRenderTextureSize: 512,
maxVertexAttribs: 16,
maxVaryingVectors: 16,
maxDrawBuffers: 8,
maxFragmentUniformVectors: 16,
maxVertexUniformVectors: 16,
standardDerivatives: true,
astc: null,
pvrtc: null,
etc1: null,
etc2: null,
bptc: null,
maxAnisotropy: 16, // TODO: Retrieve this smartly. Currently set to D3D11 maximum allowable value.
uintIndices: true,
fragmentDepthSupported: false,
highPrecisionShaderSupported: true,
colorBufferFloat: false,
supportFloatTexturesResolve: false,
rg11b10ufColorRenderable: false,
textureFloat: true,
textureFloatLinearFiltering: true,
textureFloatRender: true,
textureHalfFloat: true,
textureHalfFloatLinearFiltering: true,
textureHalfFloatRender: true,
textureLOD: true,
texelFetch: false,
drawBuffersExtension: false,
depthTextureExtension: false,
vertexArrayObject: true,
instancedArrays: true,
supportOcclusionQuery: false,
canUseTimestampForTimerQuery: false,
blendMinMax: false,
maxMSAASamples: 16,
canUseGLInstanceID: true,
canUseGLVertexID: true,
supportComputeShaders: false,
supportSRGBBuffers: true,
supportTransformFeedbacks: false,
textureMaxLevel: false,
texture2DArrayMaxLayerCount: _native.Engine.CAPS_LIMITS_MAX_TEXTURE_LAYERS,
disableMorphTargetTexture: false,
parallelShaderCompile: { COMPLETION_STATUS_KHR: 0 },
textureNorm16: false,
blendParametersPerTarget: false,
dualSourceBlending: false,
};
this._features = {
forceBitmapOverHTMLImageElement: true,
supportRenderAndCopyToLodForFloatTextures: false,
supportDepthStencilTexture: false,
supportShadowSamplers: false,
uniformBufferHardCheckMatrix: false,
allowTexturePrefiltering: false,
trackUbosInFrame: false,
checkUbosContentBeforeUpload: false,
supportCSM: false,
basisNeedsPOT: false,
support3DTextures: false,
needTypeSuffixInShaderConstants: false,
supportMSAA: true,
supportSSAO2: false,
supportIBLShadows: false,
supportExtendedTextureFormats: false,
supportSwitchCaseInShader: false,
supportSyncTextureRead: false,
needsInvertingBitmap: true,
useUBOBindingCache: true,
needShaderCodeInlining: true,
needToAlwaysBindUniformBuffers: false,
supportRenderPasses: true,
supportSpriteInstancing: false,
forceVertexBufferStrideAndOffsetMultiple4Bytes: true,
_checkNonFloatVertexBuffersDontRecreatePipelineContext: false,
_collectUbosUpdatedInFrame: false,
};
Tools.Log("Babylon Native (v" + Engine.Version + ") launched");
Tools.LoadScript = function (scriptUrl, onSuccess, onError, scriptId) {
Tools.LoadFile(scriptUrl, (data) => {
Function(data).apply(null);
if (onSuccess) {
onSuccess();
}
}, undefined, undefined, false, (request, exception) => {
if (onError) {
onError("LoadScript Error", exception);
}
});
};
// Wrappers
if (typeof URL === "undefined") {
window.URL = {
createObjectURL: function () { },
revokeObjectURL: function () { },
};
}
if (typeof Blob === "undefined") {
window.Blob = function (v) {
return v;
};
}
// polyfill for Chakra
if (!Array.prototype.flat) {
Object.defineProperty(Array.prototype, "flat", {
configurable: true,
value: function flat() {
const depth = isNaN(arguments[0]) ? 1 : Number(arguments[0]);
return depth
? Array.prototype.reduce.call(this, function (acc, cur) {
if (Array.isArray(cur)) {
// eslint-disable-next-line prefer-spread
acc.push.apply(acc, flat.call(cur, depth - 1));
}
else {
acc.push(cur);
}
return acc;
}, [])
: Array.prototype.slice.call(this);
},
writable: true,
});
}
// Currently we do not fully configure the ThinEngine on construction of NativeEngine.
// Setup resolution scaling based on display settings.
const devicePixelRatio = window ? window.devicePixelRatio || 1.0 : 1.0;
this._hardwareScalingLevel = options.adaptToDeviceRatio ? 1.0 / devicePixelRatio : 1.0;
this._engine.setHardwareScalingLevel(this._hardwareScalingLevel);
this._lastDevicePixelRatio = devicePixelRatio;
this.resize();
const currentDepthFunction = this.getDepthFunction();
if (currentDepthFunction) {
this.setDepthFunction(currentDepthFunction);
}
// Shader processor
this._shaderProcessor = new NativeShaderProcessor();
this.onNewSceneAddedObservable.add((scene) => {
const originalRender = scene.render;
scene.render = (...args) => {
this._commandBufferEncoder.beginCommandScope();
originalRender.apply(scene, args);
this._commandBufferEncoder.endCommandScope();
};
});
}
dispose() {
super.dispose();
if (this._boundBuffersVertexArray) {
this._deleteVertexArray(this._boundBuffersVertexArray);
}
this._engine.dispose();
}
/** @internal */
static _createNativeDataStream() {
return new NativeDataStream();
}
/**
* Can be used to override the current requestAnimationFrame requester.
* @internal
*/
_queueNewFrame(bindedRenderFunction, requester) {
// Use the provided requestAnimationFrame, unless the requester is the window. In that case, we will default to the Babylon Native version of requestAnimationFrame.
if (requester.requestAnimationFrame && requester !== window) {
requester.requestAnimationFrame(bindedRenderFunction);
}
else {
this._engine.requestAnimationFrame(bindedRenderFunction);
}
return 0;
}
_restoreEngineAfterContextLost() {
this._clearEmptyResources();
const depthTest = this._depthCullingState.depthTest; // backup those values because the call to initEngine / wipeCaches will reset them
const depthFunc = this._depthCullingState.depthFunc;
const depthMask = this._depthCullingState.depthMask;
const stencilTest = this._stencilState.stencilTest;
this._rebuildGraphicsResources();
this._depthCullingState.depthTest = depthTest;
this._depthCullingState.depthFunc = depthFunc;
this._depthCullingState.depthMask = depthMask;
this._stencilState.stencilTest = stencilTest;
this._flagContextRestored();
}
/**
* Override default engine behavior.
* @param framebuffer
*/
_bindUnboundFramebuffer(framebuffer) {
if (this._currentFramebuffer !== framebuffer) {
if (this._currentFramebuffer) {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_UNBINDFRAMEBUFFER);
this._commandBufferEncoder.encodeCommandArgAsNativeData(this._currentFramebuffer);
this._commandBufferEncoder.finishEncodingCommand();
}
if (framebuffer) {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_BINDFRAMEBUFFER);
this._commandBufferEncoder.encodeCommandArgAsNativeData(framebuffer);
this._commandBufferEncoder.finishEncodingCommand();
}
this._currentFramebuffer = framebuffer;
}
}
/**
* Gets host document
* @returns the host document object
*/
getHostDocument() {
return null;
}
clear(color, backBuffer, depth, stencil = false, stencilClearValue = 0) {
if (this.useReverseDepthBuffer) {
throw new Error("reverse depth buffer is not currently implemented");
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_CLEAR);
this._commandBufferEncoder.encodeCommandArgAsUInt32(backBuffer && color ? 1 : 0);
this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.r : 0);
this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.g : 0);
this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.b : 0);
this._commandBufferEncoder.encodeCommandArgAsFloat32(color ? color.a : 1);
this._commandBufferEncoder.encodeCommandArgAsUInt32(depth ? 1 : 0);
this._commandBufferEncoder.encodeCommandArgAsFloat32(1);
this._commandBufferEncoder.encodeCommandArgAsUInt32(stencil ? 1 : 0);
this._commandBufferEncoder.encodeCommandArgAsUInt32(stencilClearValue);
this._commandBufferEncoder.finishEncodingCommand();
}
createIndexBuffer(indices, updateable, _label) {
const data = this._normalizeIndexData(indices);
const buffer = new NativeDataBuffer();
buffer.references = 1;
buffer.is32Bits = data.BYTES_PER_ELEMENT === 4;
if (data.byteLength) {
buffer.nativeIndexBuffer = this._engine.createIndexBuffer(data.buffer, data.byteOffset, data.byteLength, buffer.is32Bits, updateable ?? false);
}
return buffer;
}
createVertexBuffer(vertices, updateable, _label) {
const data = ArrayBuffer.isView(vertices) ? vertices : new Float32Array(vertices);
const buffer = new NativeDataBuffer();
buffer.references = 1;
if (data.byteLength) {
buffer.nativeVertexBuffer = this._engine.createVertexBuffer(data.buffer, data.byteOffset, data.byteLength, updateable ?? false);
}
return buffer;
}
_recordVertexArrayObject(vertexArray, vertexBuffers, indexBuffer, effect, overrideVertexBuffers) {
if (!effect._checkedNonFloatVertexBuffers) {
checkNonFloatVertexBuffers(vertexBuffers, effect);
effect._checkedNonFloatVertexBuffers = true;
}
if (indexBuffer) {
this._engine.recordIndexBuffer(vertexArray, indexBuffer.nativeIndexBuffer);
}
const attributes = effect.getAttributesNames();
for (let index = 0; index < attributes.length; index++) {
const location = effect.getAttributeLocation(index);
if (location >= 0) {
const kind = attributes[index];
let vertexBuffer = null;
if (overrideVertexBuffers) {
vertexBuffer = overrideVertexBuffers[kind];
}
if (!vertexBuffer) {
vertexBuffer = vertexBuffers[kind];
}
if (vertexBuffer) {
const buffer = vertexBuffer.effectiveBuffer;
if (buffer && buffer.nativeVertexBuffer) {
this._engine.recordVertexBuffer(vertexArray, buffer.nativeVertexBuffer, location, vertexBuffer.effectiveByteOffset, vertexBuffer.effectiveByteStride, vertexBuffer.getSize(), getNativeAttribType(vertexBuffer.type), vertexBuffer.normalized, vertexBuffer.getInstanceDivisor());
}
}
}
}
}
bindBuffers(vertexBuffers, indexBuffer, effect) {
if (this._boundBuffersVertexArray) {
this._deleteVertexArray(this._boundBuffersVertexArray);
}
this._boundBuffersVertexArray = this._engine.createVertexArray();
this._recordVertexArrayObject(this._boundBuffersVertexArray, vertexBuffers, indexBuffer, effect);
this.bindVertexArrayObject(this._boundBuffersVertexArray);
}
recordVertexArrayObject(vertexBuffers, indexBuffer, effect, overrideVertexBuffers) {
const vertexArray = this._engine.createVertexArray();
this._recordVertexArrayObject(vertexArray, vertexBuffers, indexBuffer, effect, overrideVertexBuffers);
return vertexArray;
}
_deleteVertexArray(vertexArray) {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEVERTEXARRAY);
this._commandBufferEncoder.encodeCommandArgAsNativeData(vertexArray);
this._commandBufferEncoder.finishEncodingCommand();
}
bindVertexArrayObject(vertexArray) {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_BINDVERTEXARRAY);
this._commandBufferEncoder.encodeCommandArgAsNativeData(vertexArray);
this._commandBufferEncoder.finishEncodingCommand();
}
releaseVertexArrayObject(vertexArray) {
this._deleteVertexArray(vertexArray);
}
getAttributes(pipelineContext, attributesNames) {
const nativePipelineContext = pipelineContext;
const nativeShaderProcessingContext = nativePipelineContext.shaderProcessingContext;
remappedAttributesNames.length = 0;
for (let index = 0; index < attributesNames.length; index++) {
const origAttributeName = attributesNames[index];
const attributeName = nativeShaderProcessingContext.remappedAttributeNames[origAttributeName] ?? origAttributeName;
remappedAttributesNames[index] = attributeName;
}
return this._engine.getAttributes(nativePipelineContext.program, remappedAttributesNames);
}
/**
* Triangle Fan and Line Loop are not supported by modern rendering API
* @param fillMode defines the primitive to use
* @returns true if supported
*/
_checkSupportedFillMode(fillMode) {
if (fillMode == 5 || fillMode == 8) {
if (!this._fillModeWarningDisplayed) {
Logger.Warn("Line Loop and Triangle Fan are not supported fill modes with Babylon Native. Elements with these fill mode will not be visible.");
this._fillModeWarningDisplayed = true;
}
return false;
}
return true;
}
/**
* Draw a list of indexed primitives
* @param fillMode defines the primitive to use
* @param indexStart defines the starting index
* @param indexCount defines the number of index to draw
* @param instancesCount defines the number of instances to draw (if instantiation is enabled)
*/
drawElementsType(fillMode, indexStart, indexCount, instancesCount) {
if (!this._checkSupportedFillMode(fillMode)) {
return;
}
// Apply states
this._drawCalls.addCount(1, false);
if (instancesCount) {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAWINDEXEDINSTANCED);
this._commandBufferEncoder.encodeCommandArgAsUInt32(fillMode);
this._commandBufferEncoder.encodeCommandArgAsUInt32(indexStart);
this._commandBufferEncoder.encodeCommandArgAsUInt32(indexCount);
this._commandBufferEncoder.encodeCommandArgAsUInt32(instancesCount);
}
else {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAWINDEXED);
this._commandBufferEncoder.encodeCommandArgAsUInt32(fillMode);
this._commandBufferEncoder.encodeCommandArgAsUInt32(indexStart);
this._commandBufferEncoder.encodeCommandArgAsUInt32(indexCount);
}
this._commandBufferEncoder.finishEncodingCommand();
}
/**
* Draw a list of unindexed primitives
* @param fillMode defines the primitive to use
* @param verticesStart defines the index of first vertex to draw
* @param verticesCount defines the count of vertices to draw
* @param instancesCount defines the number of instances to draw (if instantiation is enabled)
*/
drawArraysType(fillMode, verticesStart, verticesCount, instancesCount) {
if (!this._checkSupportedFillMode(fillMode)) {
return;
}
// Apply states
this._drawCalls.addCount(1, false);
if (instancesCount) {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAWINSTANCED);
this._commandBufferEncoder.encodeCommandArgAsUInt32(fillMode);
this._commandBufferEncoder.encodeCommandArgAsUInt32(verticesStart);
this._commandBufferEncoder.encodeCommandArgAsUInt32(verticesCount);
this._commandBufferEncoder.encodeCommandArgAsUInt32(instancesCount);
}
else {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DRAW);
this._commandBufferEncoder.encodeCommandArgAsUInt32(fillMode);
this._commandBufferEncoder.encodeCommandArgAsUInt32(verticesStart);
this._commandBufferEncoder.encodeCommandArgAsUInt32(verticesCount);
}
this._commandBufferEncoder.finishEncodingCommand();
}
createPipelineContext(shaderProcessingContext) {
const isAsync = !!this._caps.parallelShaderCompile;
return new NativePipelineContext(this, isAsync, shaderProcessingContext);
}
createMaterialContext() {
return undefined;
}
createDrawContext() {
return undefined;
}
/**
* Function is not technically Async
* @internal
*/
// eslint-disable-next-line no-restricted-syntax
_preparePipelineContextAsync(pipelineContext, vertexSourceCode, fragmentSourceCode, createAsRaw, _rawVertexSourceCode, _rawFragmentSourceCode, _rebuildRebind, defines, _transformFeedbackVaryings, _key, onReady) {
if (createAsRaw) {
this.createRawShaderProgram();
}
else {
this.createShaderProgram(pipelineContext, vertexSourceCode, fragmentSourceCode, defines);
}
onReady();
}
/**
* @internal
*/
_getShaderProcessingContext(_shaderLanguage) {
return new NativeShaderProcessingContext();
}
/**
* @internal
*/
_executeWhenRenderingStateIsCompiled(pipelineContext, action) {
const nativePipelineContext = pipelineContext;
if (nativePipelineContext.isAsync) {
if (nativePipelineContext.onCompiled) {
const oldHandler = nativePipelineContext.onCompiled;
nativePipelineContext.onCompiled = () => {
oldHandler();
action();
};
}
else {
nativePipelineContext.onCompiled = action;
}
}
else {
action();
}
}
createRawShaderProgram() {
throw new Error("Not Supported");
}
createShaderProgram(pipelineContext, vertexCode, fragmentCode, defines) {
const nativePipelineContext = pipelineContext;
this.onBeforeShaderCompilationObservable.notifyObservers(this);
const vertexInliner = new ShaderCodeInliner(vertexCode);
vertexInliner.processCode();
vertexCode = vertexInliner.code;
const fragmentInliner = new ShaderCodeInliner(fragmentCode);
fragmentInliner.processCode();
fragmentCode = fragmentInliner.code;
vertexCode = ThinEngine._ConcatenateShader(vertexCode, defines);
fragmentCode = ThinEngine._ConcatenateShader(fragmentCode, defines);
const onSuccess = () => {
nativePipelineContext.isCompiled = true;
nativePipelineContext.onCompiled?.();
this.onAfterShaderCompilationObservable.notifyObservers(this);
};
if (pipelineContext.isAsync) {
nativePipelineContext.program = this._engine.createProgramAsync(vertexCode, fragmentCode, onSuccess, (error) => {
nativePipelineContext.compilationError = error;
});
}
else {
try {
nativePipelineContext.program = this._engine.createProgram(vertexCode, fragmentCode);
onSuccess();
}
catch (e) {
const message = e?.message;
throw new Error("SHADER ERROR" + (typeof message === "string" ? "\n" + message : ""));
}
}
return nativePipelineContext.program;
}
/**
* Inline functions in shader code that are marked to be inlined
* @param code code to inline
* @returns inlined code
*/
inlineShaderCode(code) {
const sci = new ShaderCodeInliner(code);
sci.debug = false;
sci.processCode();
return sci.code;
}
_setProgram(program) {
if (this._currentProgram !== program) {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETPROGRAM);
this._commandBufferEncoder.encodeCommandArgAsNativeData(program);
this._commandBufferEncoder.finishEncodingCommand();
this._currentProgram = program;
}
}
_deletePipelineContext(pipelineContext) {
const nativePipelineContext = pipelineContext;
if (nativePipelineContext && nativePipelineContext.program) {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_DELETEPROGRAM);
this._commandBufferEncoder.encodeCommandArgAsNativeData(nativePipelineContext.program);
this._commandBufferEncoder.finishEncodingCommand();
}
}
getUniforms(pipelineContext, uniformsNames) {
const nativePipelineContext = pipelineContext;
return this._engine.getUniforms(nativePipelineContext.program, uniformsNames);
}
bindUniformBlock(pipelineContext, blockName, index) {
// TODO
throw new Error("Not Implemented");
}
bindSamplers(effect) {
const nativePipelineContext = effect.getPipelineContext();
this._setProgram(nativePipelineContext.program);
// TODO: share this with engine?
const samplers = effect.getSamplers();
for (let index = 0; index < samplers.length; index++) {
const uniform = effect.getUniform(samplers[index]);
if (uniform) {
this._boundUniforms[index] = uniform;
}
}
this._currentEffect = null;
}
getRenderWidth(useScreen = false) {
if (!useScreen && this._currentRenderTarget) {
return this._currentRenderTarget.width;
}
return this._engine.getRenderWidth();
}
getRenderHeight(useScreen = false) {
if (!useScreen && this._currentRenderTarget) {
return this._currentRenderTarget.height;
}
return this._engine.getRenderHeight();
}
setViewport(viewport, requiredWidth, requiredHeight) {
this._cachedViewport = viewport;
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETVIEWPORT);
this._commandBufferEncoder.encodeCommandArgAsFloat32(viewport.x);
this._commandBufferEncoder.encodeCommandArgAsFloat32(viewport.y);
this._commandBufferEncoder.encodeCommandArgAsFloat32(viewport.width);
this._commandBufferEncoder.encodeCommandArgAsFloat32(viewport.height);
this._commandBufferEncoder.finishEncodingCommand();
}
enableScissor(x, y, width, height) {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETSCISSOR);
this._commandBufferEncoder.encodeCommandArgAsFloat32(x);
this._commandBufferEncoder.encodeCommandArgAsFloat32(y);
this._commandBufferEncoder.encodeCommandArgAsFloat32(width);
this._commandBufferEncoder.encodeCommandArgAsFloat32(height);
this._commandBufferEncoder.finishEncodingCommand();
}
disableScissor() {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETSCISSOR);
this._commandBufferEncoder.encodeCommandArgAsFloat32(0);
this._commandBufferEncoder.encodeCommandArgAsFloat32(0);
this._commandBufferEncoder.encodeCommandArgAsFloat32(0);
this._commandBufferEncoder.encodeCommandArgAsFloat32(0);
this._commandBufferEncoder.finishEncodingCommand();
}
setStateCullFaceType(_cullBackFaces, _force) {
throw new Error("setStateCullFaceType: Not Implemented");
}
setState(culling, zOffset = 0, force, reverseSide = false, cullBackFaces, stencil, zOffsetUnits = 0) {
this._zOffset = zOffset;
this._zOffsetUnits = zOffsetUnits;
if (this._zOffset !== 0) {
Tools.Warn("zOffset is not supported in Native engine.");
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETSTATE);
this._commandBufferEncoder.encodeCommandArgAsUInt32(culling ? 1 : 0);
this._commandBufferEncoder.encodeCommandArgAsFloat32(zOffset);
this._commandBufferEncoder.encodeCommandArgAsFloat32(zOffsetUnits);
this._commandBufferEncoder.encodeCommandArgAsUInt32((this.cullBackFaces ?? cullBackFaces ?? true) ? 1 : 0);
this._commandBufferEncoder.encodeCommandArgAsUInt32(reverseSide ? 1 : 0);
this._commandBufferEncoder.finishEncodingCommand();
}
/**
* Gets the client rect of native canvas. Needed for InputManager.
* @returns a client rectangle
*/
getInputElementClientRect() {
const rect = {
bottom: this.getRenderHeight(),
height: this.getRenderHeight(),
left: 0,
right: this.getRenderWidth(),
top: 0,
width: this.getRenderWidth(),
x: 0,
y: 0,
toJSON: () => { },
};
return rect;
}
/**
* Set the z offset Factor to apply to current rendering
* @param value defines the offset to apply
*/
setZOffset(value) {
if (value !== this._zOffset) {
this._zOffset = value;
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETZOFFSET);
this._commandBufferEncoder.encodeCommandArgAsFloat32(this.useReverseDepthBuffer ? -value : value);
this._commandBufferEncoder.finishEncodingCommand();
}
}
/**
* Gets the current value of the zOffset Factor
* @returns the current zOffset Factor state
*/
getZOffset() {
return this._zOffset;
}
/**
* Set the z offset Units to apply to current rendering
* @param value defines the offset to apply
*/
setZOffsetUnits(value) {
if (value !== this._zOffsetUnits) {
this._zOffsetUnits = value;
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETZOFFSETUNITS);
this._commandBufferEncoder.encodeCommandArgAsFloat32(this.useReverseDepthBuffer ? -value : value);
this._commandBufferEncoder.finishEncodingCommand();
}
}
/**
* Gets the current value of the zOffset Units
* @returns the current zOffset Units state
*/
getZOffsetUnits() {
return this._zOffsetUnits;
}
/**
* Enable or disable depth buffering
* @param enable defines the state to set
*/
setDepthBuffer(enable) {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETDEPTHTEST);
this._commandBufferEncoder.encodeCommandArgAsUInt32(enable ? this._currentDepthTest : _native.Engine.DEPTH_TEST_ALWAYS);
this._commandBufferEncoder.finishEncodingCommand();
}
/**
* Gets a boolean indicating if depth writing is enabled
* @returns the current depth writing state
*/
getDepthWrite() {
return this._depthWrite;
}
getDepthFunction() {
switch (this._currentDepthTest) {
case _native.Engine.DEPTH_TEST_NEVER:
return 512;
case _native.Engine.DEPTH_TEST_ALWAYS:
return 519;
case _native.Engine.DEPTH_TEST_GREATER:
return 516;
case _native.Engine.DEPTH_TEST_GEQUAL:
return 518;
case _native.Engine.DEPTH_TEST_NOTEQUAL:
return 517;
case _native.Engine.DEPTH_TEST_EQUAL:
return 514;
case _native.Engine.DEPTH_TEST_LESS:
return 513;
case _native.Engine.DEPTH_TEST_LEQUAL:
return 515;
}
return null;
}
setDepthFunction(depthFunc) {
let nativeDepthFunc = 0;
switch (depthFunc) {
case 512:
nativeDepthFunc = _native.Engine.DEPTH_TEST_NEVER;
break;
case 519:
nativeDepthFunc = _native.Engine.DEPTH_TEST_ALWAYS;
break;
case 516:
nativeDepthFunc = _native.Engine.DEPTH_TEST_GREATER;
break;
case 518:
nativeDepthFunc = _native.Engine.DEPTH_TEST_GEQUAL;
break;
case 517:
nativeDepthFunc = _native.Engine.DEPTH_TEST_NOTEQUAL;
break;
case 514:
nativeDepthFunc = _native.Engine.DEPTH_TEST_EQUAL;
break;
case 513:
nativeDepthFunc = _native.Engine.DEPTH_TEST_LESS;
break;
case 515:
nativeDepthFunc = _native.Engine.DEPTH_TEST_LEQUAL;
break;
}
this._currentDepthTest = nativeDepthFunc;
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETDEPTHTEST);
this._commandBufferEncoder.encodeCommandArgAsUInt32(this._currentDepthTest);
this._commandBufferEncoder.finishEncodingCommand();
}
/**
* Enable or disable depth writing
* @param enable defines the state to set
*/
setDepthWrite(enable) {
this._depthWrite = enable;
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETDEPTHWRITE);
this._commandBufferEncoder.encodeCommandArgAsUInt32(Number(enable));
this._commandBufferEncoder.finishEncodingCommand();
}
/**
* Enable or disable color writing
* @param enable defines the state to set
*/
setColorWrite(enable) {
this._colorWrite = enable;
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETCOLORWRITE);
this._commandBufferEncoder.encodeCommandArgAsUInt32(Number(enable));
this._commandBufferEncoder.finishEncodingCommand();
}
/**
* Gets a boolean indicating if color writing is enabled
* @returns the current color writing state
*/
getColorWrite() {
return this._colorWrite;
}
applyStencil() {
this._setStencil(this._stencilMask, getNativeStencilOpFail(this._stencilOpStencilFail), getNativeStencilDepthFail(this._stencilOpDepthFail), getNativeStencilDepthPass(this._stencilOpStencilDepthPass), getNativeStencilFunc(this._stencilFunc), this._stencilFuncRef);
}
_setStencil(mask, stencilOpFail, depthOpFail, depthOpPass, func, ref) {
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETSTENCIL);
this._commandBufferEncoder.encodeCommandArgAsUInt32(mask);
this._commandBufferEncoder.encodeCommandArgAsUInt32(stencilOpFail);
this._commandBufferEncoder.encodeCommandArgAsUInt32(depthOpFail);
this._commandBufferEncoder.encodeCommandArgAsUInt32(depthOpPass);
this._commandBufferEncoder.encodeCommandArgAsUInt32(func);
this._commandBufferEncoder.encodeCommandArgAsUInt32(ref);
this._commandBufferEncoder.finishEncodingCommand();
}
/**
* Enable or disable the stencil buffer
* @param enable defines if the stencil buffer must be enabled or disabled
*/
setStencilBuffer(enable) {
this._stencilTest = enable;
if (enable) {
this.applyStencil();
}
else {
this._setStencil(255, _native.Engine.STENCIL_OP_FAIL_S_KEEP, _native.Engine.STENCIL_OP_FAIL_Z_KEEP, _native.Engine.STENCIL_OP_PASS_Z_KEEP, _native.Engine.STENCIL_TEST_ALWAYS, 0);
}
}
/**
* Gets a boolean indicating if stencil buffer is enabled
* @returns the current stencil buffer state
*/
getStencilBuffer() {
return this._stencilTest;
}
/**
* Gets the current stencil operation when stencil passes
* @returns a number defining stencil operation to use when stencil passes
*/
getStencilOperationPass() {
return this._stencilOpStencilDepthPass;
}
/**
* Sets the stencil operation to use when stencil passes
* @param operation defines the stencil operation to use when stencil passes
*/
setStencilOperationPass(operation) {
this._stencilOpStencilDepthPass = operation;
this.applyStencil();
}
/**
* Sets the current stencil mask
* @param mask defines the new stencil mask to use
*/
setStencilMask(mask) {
this._stencilMask = mask;
this.applyStencil();
}
/**
* Sets the current stencil function
* @param stencilFunc defines the new stencil function to use
*/
setStencilFunction(stencilFunc) {
this._stencilFunc = stencilFunc;
this.applyStencil();
}
/**
* Sets the current stencil reference
* @param reference defines the new stencil reference to use
*/
setStencilFunctionReference(reference) {
this._stencilFuncRef = reference;
this.applyStencil();
}
/**
* Sets the current stencil mask
* @param mask defines the new stencil mask to use
*/
setStencilFunctionMask(mask) {
this._stencilFuncMask = mask;
}
/**
* Sets the stencil operation to use when stencil fails
* @param operation defines the stencil operation to use when stencil fails
*/
setStencilOperationFail(operation) {
this._stencilOpStencilFail = operation;
this.applyStencil();
}
/**
* Sets the stencil operation to use when depth fails
* @param operation defines the stencil operation to use when depth fails
*/
setStencilOperationDepthFail(operation) {
this._stencilOpDepthFail = operation;
this.applyStencil();
}
/**
* Gets the current stencil mask
* @returns a number defining the new stencil mask to use
*/
getStencilMask() {
return this._stencilMask;
}
/**
* Gets the current stencil function
* @returns a number defining the stencil function to use
*/
getStencilFunction() {
return this._stencilFunc;
}
/**
* Gets the current stencil reference value
* @returns a number defining the stencil reference value to use
*/
getStencilFunctionReference() {
return this._stencilFuncRef;
}
/**
* Gets the current stencil mask
* @returns a number defining the stencil mask to use
*/
getStencilFunctionMask() {
return this._stencilFuncMask;
}
/**
* Gets the current stencil operation when stencil fails
* @returns a number defining stencil operation to use when stencil fails
*/
getStencilOperationFail() {
return this._stencilOpStencilFail;
}
/**
* Gets the current stencil operation when depth fails
* @returns a number defining stencil operation to use when depth fails
*/
getStencilOperationDepthFail() {
return this._stencilOpDepthFail;
}
/**
* Sets alpha constants used by some alpha blending modes
* @param r defines the red component
* @param g defines the green component
* @param b defines the blue component
* @param a defines the alpha component
*/
setAlphaConstants(r, g, b, a) {
throw new Error("Setting alpha blend constant color not yet implemented.");
}
/**
* Sets the current alpha mode
* @param mode defines the mode to use (one of the BABYLON.undefined)
* @param noDepthWriteChange defines if depth writing state should remains unchanged (false by default)
* @param targetIndex defines the index of the target to set the alpha mode for (default is 0)
* @see https://doc.babylonjs.com/features/featuresDeepDive/materials/advanced/transparent_rendering
*/
setAlphaMode(mode, noDepthWriteChange = false, targetIndex = 0) {
if (this._alphaMode[targetIndex] === mode) {
return;
}
const nativeMode = getNativeAlphaMode(mode);
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETBLENDMODE);
this._commandBufferEncoder.encodeCommandArgAsUInt32(nativeMode);
this._commandBufferEncoder.finishEncodingCommand();
if (!noDepthWriteChange) {
this.setDepthWrite(mode === 0);
}
this._alphaMode[targetIndex] = mode;
}
setInt(uniform, int) {
if (!uniform) {
return false;
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETINT);
this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform);
this._commandBufferEncoder.encodeCommandArgAsInt32(int);
this._commandBufferEncoder.finishEncodingCommand();
return true;
}
setIntArray(uniform, array) {
if (!uniform) {
return false;
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETINTARRAY);
this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform);
this._commandBufferEncoder.encodeCommandArgAsInt32s(array);
this._commandBufferEncoder.finishEncodingCommand();
return true;
}
setIntArray2(uniform, array) {
if (!uniform) {
return false;
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETINTARRAY2);
this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform);
this._commandBufferEncoder.encodeCommandArgAsInt32s(array);
this._commandBufferEncoder.finishEncodingCommand();
return true;
}
setIntArray3(uniform, array) {
if (!uniform) {
return false;
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETINTARRAY3);
this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform);
this._commandBufferEncoder.encodeCommandArgAsInt32s(array);
this._commandBufferEncoder.finishEncodingCommand();
return true;
}
setIntArray4(uniform, array) {
if (!uniform) {
return false;
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETINTARRAY4);
this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform);
this._commandBufferEncoder.encodeCommandArgAsInt32s(array);
this._commandBufferEncoder.finishEncodingCommand();
return true;
}
setFloatArray(uniform, array) {
if (!uniform) {
return false;
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETFLOATARRAY);
this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform);
this._commandBufferEncoder.encodeCommandArgAsFloat32s(array);
this._commandBufferEncoder.finishEncodingCommand();
return true;
}
setFloatArray2(uniform, array) {
if (!uniform) {
return false;
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETFLOATARRAY2);
this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform);
this._commandBufferEncoder.encodeCommandArgAsFloat32s(array);
this._commandBufferEncoder.finishEncodingCommand();
return true;
}
setFloatArray3(uniform, array) {
if (!uniform) {
return false;
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETFLOATARRAY3);
this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform);
this._commandBufferEncoder.encodeCommandArgAsFloat32s(array);
this._commandBufferEncoder.finishEncodingCommand();
return true;
}
setFloatArray4(uniform, array) {
if (!uniform) {
return false;
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETFLOATARRAY4);
this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform);
this._commandBufferEncoder.encodeCommandArgAsFloat32s(array);
this._commandBufferEncoder.finishEncodingCommand();
return true;
}
setArray(uniform, array) {
if (!uniform) {
return false;
}
return this.setFloatArray(uniform, new Float32Array(array));
}
setArray2(uniform, array) {
if (!uniform) {
return false;
}
return this.setFloatArray2(uniform, new Float32Array(array));
}
setArray3(uniform, array) {
if (!uniform) {
return false;
}
return this.setFloatArray3(uniform, new Float32Array(array));
}
setArray4(uniform, array) {
if (!uniform) {
return false;
}
return this.setFloatArray4(uniform, new Float32Array(array));
}
setMatrices(uniform, matrices) {
if (!uniform) {
return false;
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETMATRICES);
this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform);
this._commandBufferEncoder.encodeCommandArgAsFloat32s(matrices);
this._commandBufferEncoder.finishEncodingCommand();
return true;
}
setMatrix3x3(uniform, matrix) {
if (!uniform) {
return false;
}
this._commandBufferEncoder.startEncodingCommand(_native.Engine.COMMAND_SETMATRIX3X3);
this._commandBufferEncoder.encodeCommandArgAsNativeData(uniform);
this._commandBufferEncoder.encodeCommandArgAsFloat32s(matrix);
this._commandBufferEncoder.finishEncodingCommand();