diff --git a/.build/flags.json b/.build/flags.json index f2280cc67a65..cd0d5d665302 100644 --- a/.build/flags.json +++ b/.build/flags.json @@ -1,3 +1,3 @@ { - "errorThreshold": 1.2 + "errorThreshold": 1.1 } diff --git a/packages/dev/core/src/Buffers/buffer.pure.ts b/packages/dev/core/src/Buffers/buffer.pure.ts index 660e8d4fbefc..186e00ac2f84 100644 --- a/packages/dev/core/src/Buffers/buffer.pure.ts +++ b/packages/dev/core/src/Buffers/buffer.pure.ts @@ -999,17 +999,3 @@ export function RegisterVertexBuffer(): void { VertexBuffer.ForEach = VertexBufferForEach; VertexBuffer.GetFloatData = VertexBufferGetFloatData; } - -// #region GENERATED_SIDE_EFFECT_STUBS — do not edit, regenerate with `npm run generate:side-effect-stubs` -import { _MissingSideEffectProperty } from "../Misc/devTools"; - -if (!Object.getOwnPropertyDescriptor(VertexBuffer.prototype, "effectiveByteStride")) { - Object.defineProperty(VertexBuffer.prototype, "effectiveByteStride", _MissingSideEffectProperty("VertexBuffer", "effectiveByteStride")); -} -if (!Object.getOwnPropertyDescriptor(VertexBuffer.prototype, "effectiveByteOffset")) { - Object.defineProperty(VertexBuffer.prototype, "effectiveByteOffset", _MissingSideEffectProperty("VertexBuffer", "effectiveByteOffset")); -} -if (!Object.getOwnPropertyDescriptor(VertexBuffer.prototype, "effectiveBuffer")) { - Object.defineProperty(VertexBuffer.prototype, "effectiveBuffer", _MissingSideEffectProperty("VertexBuffer", "effectiveBuffer")); -} -// #endregion GENERATED_SIDE_EFFECT_STUBS diff --git a/packages/dev/core/src/Engines/AbstractEngine/abstractEngine.loadFile.pure.ts b/packages/dev/core/src/Engines/AbstractEngine/abstractEngine.loadFile.pure.ts index 24a3ab503b4a..39b882b9754d 100644 --- a/packages/dev/core/src/Engines/AbstractEngine/abstractEngine.loadFile.pure.ts +++ b/packages/dev/core/src/Engines/AbstractEngine/abstractEngine.loadFile.pure.ts @@ -2,6 +2,7 @@ import { type IOfflineProvider } from "../../Offline/IOfflineProvider"; import { AbstractEngine } from "../../Engines/abstractEngine.pure"; +import { RegisterFileTools } from "../../Misc/fileTools.pure"; let _Registered = false; /** @@ -14,6 +15,14 @@ export function RegisterAbstractEngineLoadFile(): void { } _Registered = true; + // File loading requires the fileTools implementation (LoadFile / LoadImage). + // These are injected lazily through EngineFunctionContext so that engines + // that never load files (e.g. the minimal thin engine) do not force-link + // fileTools. Any consumer of the loadFile extension — the full Engine + // side-effect wrapper and RegisterStandardEngineExtensions — reaches this + // registration, which wires the fileTools loaders in. + RegisterFileTools(); + AbstractEngine.prototype._loadFileAsync = async function (url: string, offlineProvider?: IOfflineProvider, useArrayBuffer?: boolean): Promise { return await new Promise((resolve, reject) => { this._loadFile( diff --git a/packages/dev/core/src/Engines/Processors/shaderProcessor.ts b/packages/dev/core/src/Engines/Processors/shaderProcessor.ts index 7003c41eae95..047fbdcf6698 100644 --- a/packages/dev/core/src/Engines/Processors/shaderProcessor.ts +++ b/packages/dev/core/src/Engines/Processors/shaderProcessor.ts @@ -15,7 +15,7 @@ import { type WebRequest } from "../../Misc/webRequest"; import { type LoadFileError } from "../../Misc/fileTools"; import { type IOfflineProvider } from "../../Offline/IOfflineProvider"; import { type IFileRequest } from "../../Misc/fileRequest"; -import { LoadFile } from "../../Misc/fileTools.pure"; +import { _WarnImport } from "../../Misc/devTools"; import { _GetGlobalDefines } from "../abstractEngine.functions"; import { type AbstractEngine } from "../abstractEngine"; @@ -499,7 +499,7 @@ export function ProcessIncludes(sourceCode: string, options: _IProcessingOptions } else { const includeShaderUrl = options.shadersRepository + "ShadersInclude/" + includeFile + ".fx"; - LoadFile(includeShaderUrl, (fileContent) => { + _FunctionContainer.loadFile(includeShaderUrl, (fileContent) => { options.includesShadersStore[includeFile] = fileContent as string; ProcessIncludes(parts.join(""), options, callback); }); @@ -522,12 +522,14 @@ export function ProcessIncludes(sourceCode: string, options: _IProcessingOptions * @internal */ export const _FunctionContainer = { - loadFile: LoadFile as ( + loadFile: ( url: string, onSuccess: (data: string | ArrayBuffer, responseURL?: string) => void, onProgress?: (ev: ProgressEvent) => void, offlineProvider?: IOfflineProvider, useArrayBuffer?: boolean, onError?: (request?: WebRequest, exception?: LoadFileError) => void - ) => IFileRequest, + ): IFileRequest => { + throw _WarnImport("FileTools"); + }, }; diff --git a/packages/dev/core/src/Engines/abstractEngine.functions.ts b/packages/dev/core/src/Engines/abstractEngine.functions.ts index d767235507fd..53ba15d5b680 100644 --- a/packages/dev/core/src/Engines/abstractEngine.functions.ts +++ b/packages/dev/core/src/Engines/abstractEngine.functions.ts @@ -5,12 +5,17 @@ import { type IWebRequest } from "core/Misc/interfaces/iWebRequest"; import { type WebRequest } from "core/Misc/webRequest"; import { type IOfflineProvider } from "core/Offline/IOfflineProvider"; import { type Nullable } from "core/types"; -import { LoadFile } from "core/Misc/fileTools.pure"; +import { _WarnImport } from "core/Misc/devTools"; import { Constants } from "./constants"; +import { type AbstractEngine } from "./abstractEngine"; /** - * @deprecated Use direct imports from fileTools.pure instead. - * Kept for backwards compatibility — will be removed in a future version. + * Injection seam for file/image loading. The implementations live in + * `fileTools.pure` and are registered here as a side effect by `fileTools.ts` + * (via `RegisterFileTools`). Keeping them behind this mutable context object — + * instead of importing them directly into the always-loaded engine classes — + * lets bundlers tree-shake all file-loading code out of builds that never + * import the `fileTools` side effect (e.g. a minimal thin-engine build). * @internal */ export const EngineFunctionContext: { @@ -22,6 +27,15 @@ export const EngineFunctionContext: { useArrayBuffer?: boolean, onError?: (request?: WebRequest, exception?: LoadFileError) => void ) => IFileRequest; + loadImage?: ( + input: string | ArrayBuffer | ArrayBufferView | Blob, + onLoad: (img: HTMLImageElement | ImageBitmap) => void, + onError: (message?: string, exception?: any) => void, + offlineProvider: Nullable, + mimeType?: string, + imageBitmapOptions?: ImageBitmapOptions, + engine?: Nullable + ) => Nullable; } = {}; /** @@ -50,7 +64,11 @@ export function _LoadFile( onError?: (request?: WebRequest, exception?: LoadFileError) => void ) => IFileRequest ): IFileRequest { - const loadFileFn = injectedLoadFile || EngineFunctionContext.loadFile || LoadFile; + const loadFileFn = injectedLoadFile || EngineFunctionContext.loadFile; + if (!loadFileFn) { + // The fileTools side effect was never imported, so no loader is registered. + throw _WarnImport("FileTools"); + } const request = loadFileFn(url, onSuccess, onProgress, offlineProvider, useArrayBuffer, onError); return request; } diff --git a/packages/dev/core/src/Engines/abstractEngine.pure.ts b/packages/dev/core/src/Engines/abstractEngine.pure.ts index 8e7082257069..27ccb61a62e0 100644 --- a/packages/dev/core/src/Engines/abstractEngine.pure.ts +++ b/packages/dev/core/src/Engines/abstractEngine.pure.ts @@ -22,7 +22,7 @@ import { type IOfflineProvider } from "../Offline/IOfflineProvider"; import { type IWebRequest } from "../Misc/interfaces/iWebRequest"; import { type IFileRequest } from "../Misc/fileRequest"; import { type Texture } from "../Materials/Textures/texture.pure"; -import { type LoadFileError, LoadImage, LoadFile as _PureLoadFile } from "../Misc/fileTools.pure"; +import { type LoadFileError } from "../Misc/fileTools.pure"; import { type _IShaderProcessingContext } from "./Processors/shaderProcessingOptions"; import { type IPipelineContext } from "./IPipelineContext"; import { type ThinTexture } from "../Materials/Textures/thinTexture"; @@ -46,7 +46,7 @@ import { DepthCullingState } from "../States/depthCullingState"; import { StencilStateComposer } from "../States/stencilStateComposer"; import { StencilState } from "../States/stencilState"; import { AlphaState } from "../States/alphaCullingState"; -import { _WarnImport, _MissingSideEffect, _MissingSideEffectProperty } from "../Misc/devTools"; +import { _WarnImport } from "../Misc/devTools"; import { InternalTexture, InternalTextureSource } from "../Materials/Textures/internalTexture"; import { IsDocumentAvailable, IsNavigatorAvailable, IsWindowObjectExist } from "../Misc/domManagement"; import { Constants } from "./constants"; @@ -1818,7 +1818,7 @@ export abstract class AbstractEngine { if (buffer && (typeof (buffer).decoding === "string" || (buffer).close)) { onload(buffer); } else { - LoadImage( + AbstractEngine._FileToolsLoadImage( url || "", onload, onInternalError, @@ -1829,7 +1829,7 @@ export abstract class AbstractEngine { ); } } else if (typeof buffer === "string" || buffer instanceof ArrayBuffer || ArrayBuffer.isView(buffer) || buffer instanceof Blob) { - LoadImage( + AbstractEngine._FileToolsLoadImage( buffer, onload, onInternalError, @@ -2688,7 +2688,11 @@ export abstract class AbstractEngine { imageBitmapOptions?: ImageBitmapOptions, engine?: AbstractEngine ): Nullable { - return LoadImage(input, onLoad, onError, offlineProvider, mimeType, imageBitmapOptions, engine); + if (!EngineFunctionContext.loadImage) { + // The fileTools side effect was never imported, so no loader is registered. + throw _WarnImport("FileTools"); + } + return EngineFunctionContext.loadImage(input, onLoad, onError, offlineProvider, mimeType, imageBitmapOptions, engine); } /** @@ -2735,7 +2739,8 @@ export abstract class AbstractEngine { if (EngineFunctionContext.loadFile) { return EngineFunctionContext.loadFile(url, onSuccess, onProgress, offlineProvider, useArrayBuffer, onError); } - return _PureLoadFile(url, onSuccess, onProgress, offlineProvider, useArrayBuffer, onError); + // The fileTools side effect was never imported, so no loader is registered. + throw _WarnImport("FileTools"); } /** @@ -2913,129 +2918,3 @@ export abstract class AbstractEngine { return null; } } - -// #region GENERATED_SIDE_EFFECT_STUBS — do not edit, regenerate with `npm run generate:side-effect-stubs` - -AbstractEngine.prototype.setAlphaEquation ??= _MissingSideEffect("AbstractEngine", "setAlphaEquation") as any; -AbstractEngine.prototype.createCubeTextureBase ??= _MissingSideEffect("AbstractEngine", "createCubeTextureBase") as any; -AbstractEngine.prototype.getInputElement ??= _MissingSideEffect("AbstractEngine", "getInputElement") as any; -AbstractEngine.prototype.getRenderingCanvasClientRect ??= _MissingSideEffect("AbstractEngine", "getRenderingCanvasClientRect") as any; -AbstractEngine.prototype.getInputElementClientRect ??= _MissingSideEffect("AbstractEngine", "getInputElementClientRect") as any; -AbstractEngine.prototype.getAspectRatio ??= _MissingSideEffect("AbstractEngine", "getAspectRatio") as any; -AbstractEngine.prototype.getScreenAspectRatio ??= _MissingSideEffect("AbstractEngine", "getScreenAspectRatio") as any; -AbstractEngine.prototype.switchFullscreen ??= _MissingSideEffect("AbstractEngine", "switchFullscreen") as any; -AbstractEngine.prototype.enterFullscreen ??= _MissingSideEffect("AbstractEngine", "enterFullscreen") as any; -AbstractEngine.prototype.exitFullscreen ??= _MissingSideEffect("AbstractEngine", "exitFullscreen") as any; -AbstractEngine.prototype.displayLoadingUI ??= _MissingSideEffect("AbstractEngine", "displayLoadingUI") as any; -AbstractEngine.prototype.hideLoadingUI ??= _MissingSideEffect("AbstractEngine", "hideLoadingUI") as any; -AbstractEngine.prototype.createQuery ??= _MissingSideEffect("AbstractEngine", "createQuery") as any; -AbstractEngine.prototype.deleteQuery ??= _MissingSideEffect("AbstractEngine", "deleteQuery") as any; -AbstractEngine.prototype.isQueryResultAvailable ??= _MissingSideEffect("AbstractEngine", "isQueryResultAvailable") as any; -AbstractEngine.prototype.getQueryResult ??= _MissingSideEffect("AbstractEngine", "getQueryResult") as any; -AbstractEngine.prototype.beginOcclusionQuery ??= _MissingSideEffect("AbstractEngine", "beginOcclusionQuery") as any; -AbstractEngine.prototype.endOcclusionQuery ??= _MissingSideEffect("AbstractEngine", "endOcclusionQuery") as any; -AbstractEngine.prototype.getRenderPassNames ??= _MissingSideEffect("AbstractEngine", "getRenderPassNames") as any; -AbstractEngine.prototype.getCurrentRenderPassName ??= _MissingSideEffect("AbstractEngine", "getCurrentRenderPassName") as any; -AbstractEngine.prototype.createRenderPassId ??= _MissingSideEffect("AbstractEngine", "createRenderPassId") as any; -AbstractEngine.prototype.releaseRenderPassId ??= _MissingSideEffect("AbstractEngine", "releaseRenderPassId") as any; -AbstractEngine.prototype.getDepthFunction ??= _MissingSideEffect("AbstractEngine", "getDepthFunction") as any; -AbstractEngine.prototype.setDepthFunction ??= _MissingSideEffect("AbstractEngine", "setDepthFunction") as any; -AbstractEngine.prototype.setDepthFunctionToGreater ??= _MissingSideEffect("AbstractEngine", "setDepthFunctionToGreater") as any; -AbstractEngine.prototype.setDepthFunctionToGreaterOrEqual ??= _MissingSideEffect("AbstractEngine", "setDepthFunctionToGreaterOrEqual") as any; -AbstractEngine.prototype.setDepthFunctionToLess ??= _MissingSideEffect("AbstractEngine", "setDepthFunctionToLess") as any; -AbstractEngine.prototype.setDepthFunctionToLessOrEqual ??= _MissingSideEffect("AbstractEngine", "setDepthFunctionToLessOrEqual") as any; -AbstractEngine.prototype.getDepthWrite ??= _MissingSideEffect("AbstractEngine", "getDepthWrite") as any; -AbstractEngine.prototype.setDepthWrite ??= _MissingSideEffect("AbstractEngine", "setDepthWrite") as any; -AbstractEngine.prototype.setAlphaConstants ??= _MissingSideEffect("AbstractEngine", "setAlphaConstants") as any; -AbstractEngine.prototype.getAlphaMode ??= _MissingSideEffect("AbstractEngine", "getAlphaMode") as any; -AbstractEngine.prototype.getAlphaEquation ??= _MissingSideEffect("AbstractEngine", "getAlphaEquation") as any; -AbstractEngine.prototype.getStencilOperationPass ??= _MissingSideEffect("AbstractEngine", "getStencilOperationPass") as any; -AbstractEngine.prototype.getStencilBackOperationPass ??= _MissingSideEffect("AbstractEngine", "getStencilBackOperationPass") as any; -AbstractEngine.prototype.getStencilBuffer ??= _MissingSideEffect("AbstractEngine", "getStencilBuffer") as any; -AbstractEngine.prototype.setStencilBuffer ??= _MissingSideEffect("AbstractEngine", "setStencilBuffer") as any; -AbstractEngine.prototype.getStencilMask ??= _MissingSideEffect("AbstractEngine", "getStencilMask") as any; -AbstractEngine.prototype.setStencilMask ??= _MissingSideEffect("AbstractEngine", "setStencilMask") as any; -AbstractEngine.prototype.getStencilFunction ??= _MissingSideEffect("AbstractEngine", "getStencilFunction") as any; -AbstractEngine.prototype.getStencilBackFunction ??= _MissingSideEffect("AbstractEngine", "getStencilBackFunction") as any; -AbstractEngine.prototype.getStencilFunctionReference ??= _MissingSideEffect("AbstractEngine", "getStencilFunctionReference") as any; -AbstractEngine.prototype.getStencilFunctionMask ??= _MissingSideEffect("AbstractEngine", "getStencilFunctionMask") as any; -AbstractEngine.prototype.setStencilFunction ??= _MissingSideEffect("AbstractEngine", "setStencilFunction") as any; -AbstractEngine.prototype.setStencilBackFunction ??= _MissingSideEffect("AbstractEngine", "setStencilBackFunction") as any; -AbstractEngine.prototype.setStencilFunctionReference ??= _MissingSideEffect("AbstractEngine", "setStencilFunctionReference") as any; -AbstractEngine.prototype.setStencilFunctionMask ??= _MissingSideEffect("AbstractEngine", "setStencilFunctionMask") as any; -AbstractEngine.prototype.getStencilOperationFail ??= _MissingSideEffect("AbstractEngine", "getStencilOperationFail") as any; -AbstractEngine.prototype.getStencilBackOperationFail ??= _MissingSideEffect("AbstractEngine", "getStencilBackOperationFail") as any; -AbstractEngine.prototype.getStencilOperationDepthFail ??= _MissingSideEffect("AbstractEngine", "getStencilOperationDepthFail") as any; -AbstractEngine.prototype.getStencilBackOperationDepthFail ??= _MissingSideEffect("AbstractEngine", "getStencilBackOperationDepthFail") as any; -AbstractEngine.prototype.setStencilOperationFail ??= _MissingSideEffect("AbstractEngine", "setStencilOperationFail") as any; -AbstractEngine.prototype.setStencilBackOperationFail ??= _MissingSideEffect("AbstractEngine", "setStencilBackOperationFail") as any; -AbstractEngine.prototype.setStencilOperationDepthFail ??= _MissingSideEffect("AbstractEngine", "setStencilOperationDepthFail") as any; -AbstractEngine.prototype.setStencilBackOperationDepthFail ??= _MissingSideEffect("AbstractEngine", "setStencilBackOperationDepthFail") as any; -AbstractEngine.prototype.setStencilOperationPass ??= _MissingSideEffect("AbstractEngine", "setStencilOperationPass") as any; -AbstractEngine.prototype.setStencilBackOperationPass ??= _MissingSideEffect("AbstractEngine", "setStencilBackOperationPass") as any; -AbstractEngine.prototype.cacheStencilState ??= _MissingSideEffect("AbstractEngine", "cacheStencilState") as any; -AbstractEngine.prototype.restoreStencilState ??= _MissingSideEffect("AbstractEngine", "restoreStencilState") as any; -AbstractEngine.prototype.createDepthStencilTexture ??= _MissingSideEffect("AbstractEngine", "createDepthStencilTexture") as any; -AbstractEngine.prototype.setCompressedTextureExclusions ??= _MissingSideEffect("AbstractEngine", "setCompressedTextureExclusions") as any; -AbstractEngine.prototype.setTextureFormatToUse ??= _MissingSideEffect("AbstractEngine", "setTextureFormatToUse") as any; -AbstractEngine.prototype.getGPUFrameTimeCounter ??= _MissingSideEffect("AbstractEngine", "getGPUFrameTimeCounter") as any; -AbstractEngine.prototype.captureGPUFrameTime ??= _MissingSideEffect("AbstractEngine", "captureGPUFrameTime") as any; -AbstractEngine.prototype.registerView ??= _MissingSideEffect("AbstractEngine", "registerView") as any; -AbstractEngine.prototype.unRegisterView ??= _MissingSideEffect("AbstractEngine", "unRegisterView") as any; -AbstractEngine.prototype.setAlphaMode ??= _MissingSideEffect("AbstractEngine", "setAlphaMode") as any; -AbstractEngine.prototype.createComputeEffect ??= _MissingSideEffect("AbstractEngine", "createComputeEffect") as any; -AbstractEngine.prototype.createComputePipelineContext ??= _MissingSideEffect("AbstractEngine", "createComputePipelineContext") as any; -AbstractEngine.prototype.createComputeContext ??= _MissingSideEffect("AbstractEngine", "createComputeContext") as any; -AbstractEngine.prototype.computeDispatch ??= _MissingSideEffect("AbstractEngine", "computeDispatch") as any; -AbstractEngine.prototype.computeDispatchIndirect ??= _MissingSideEffect("AbstractEngine", "computeDispatchIndirect") as any; -AbstractEngine.prototype.areAllComputeEffectsReady ??= _MissingSideEffect("AbstractEngine", "areAllComputeEffectsReady") as any; -AbstractEngine.prototype.releaseComputeEffects ??= _MissingSideEffect("AbstractEngine", "releaseComputeEffects") as any; -AbstractEngine.prototype.createCubeTexture ??= _MissingSideEffect("AbstractEngine", "createCubeTexture") as any; -AbstractEngine.prototype.generateMipMapsForCubemap ??= _MissingSideEffect("AbstractEngine", "generateMipMapsForCubemap") as any; -AbstractEngine.prototype.updateDynamicIndexBuffer ??= _MissingSideEffect("AbstractEngine", "updateDynamicIndexBuffer") as any; -AbstractEngine.prototype.updateDynamicVertexBuffer ??= _MissingSideEffect("AbstractEngine", "updateDynamicVertexBuffer") as any; -AbstractEngine.prototype.createDynamicTexture ??= _MissingSideEffect("AbstractEngine", "createDynamicTexture") as any; -AbstractEngine.prototype.updateDynamicTexture ??= _MissingSideEffect("AbstractEngine", "updateDynamicTexture") as any; -AbstractEngine.prototype.unBindMultiColorAttachmentFramebuffer ??= _MissingSideEffect("AbstractEngine", "unBindMultiColorAttachmentFramebuffer") as any; -AbstractEngine.prototype.createMultipleRenderTarget ??= _MissingSideEffect("AbstractEngine", "createMultipleRenderTarget") as any; -AbstractEngine.prototype.updateMultipleRenderTargetTextureSampleCount ??= _MissingSideEffect("AbstractEngine", "updateMultipleRenderTargetTextureSampleCount") as any; -AbstractEngine.prototype.generateMipMapsMultiFramebuffer ??= _MissingSideEffect("AbstractEngine", "generateMipMapsMultiFramebuffer") as any; -AbstractEngine.prototype.resolveMultiFramebuffer ??= _MissingSideEffect("AbstractEngine", "resolveMultiFramebuffer") as any; -AbstractEngine.prototype.bindAttachments ??= _MissingSideEffect("AbstractEngine", "bindAttachments") as any; -AbstractEngine.prototype.buildTextureLayout ??= _MissingSideEffect("AbstractEngine", "buildTextureLayout") as any; -AbstractEngine.prototype.restoreSingleAttachment ??= _MissingSideEffect("AbstractEngine", "restoreSingleAttachment") as any; -AbstractEngine.prototype.restoreSingleAttachmentForRenderTarget ??= _MissingSideEffect("AbstractEngine", "restoreSingleAttachmentForRenderTarget") as any; -AbstractEngine.prototype.createPrefilteredCubeTexture ??= _MissingSideEffect("AbstractEngine", "createPrefilteredCubeTexture") as any; -AbstractEngine.prototype.updateRawTexture ??= _MissingSideEffect("AbstractEngine", "updateRawTexture") as any; -AbstractEngine.prototype.updateRawCubeTexture ??= _MissingSideEffect("AbstractEngine", "updateRawCubeTexture") as any; -AbstractEngine.prototype.createRawCubeTextureFromUrl ??= _MissingSideEffect("AbstractEngine", "createRawCubeTextureFromUrl") as any; -AbstractEngine.prototype.updateRawTexture3D ??= _MissingSideEffect("AbstractEngine", "updateRawTexture3D") as any; -AbstractEngine.prototype.updateRawTexture2DArray ??= _MissingSideEffect("AbstractEngine", "updateRawTexture2DArray") as any; -AbstractEngine.prototype.createRenderTargetTexture ??= _MissingSideEffect("AbstractEngine", "createRenderTargetTexture") as any; -AbstractEngine.prototype.updateRenderTargetTextureSampleCount ??= _MissingSideEffect("AbstractEngine", "updateRenderTargetTextureSampleCount") as any; -AbstractEngine.prototype.createRenderTargetCubeTexture ??= _MissingSideEffect("AbstractEngine", "createRenderTargetCubeTexture") as any; -AbstractEngine.prototype.setDepthStencilTexture ??= _MissingSideEffect("AbstractEngine", "setDepthStencilTexture") as any; -AbstractEngine.prototype.updateVideoTexture ??= _MissingSideEffect("AbstractEngine", "updateVideoTexture") as any; -AbstractEngine.prototype.createRawCubeTexture ??= _MissingSideEffect("AbstractEngine", "createRawCubeTexture") as any; -AbstractEngine.prototype.createEffectForParticles ??= _MissingSideEffect("AbstractEngine", "createEffectForParticles") as any; -AbstractEngine.prototype.setTextureFromPostProcess ??= _MissingSideEffect("AbstractEngine", "setTextureFromPostProcess") as any; -AbstractEngine.prototype.setTextureFromPostProcessOutput ??= _MissingSideEffect("AbstractEngine", "setTextureFromPostProcessOutput") as any; -if (!Object.getOwnPropertyDescriptor(AbstractEngine.prototype, "loadingScreen")) { - Object.defineProperty(AbstractEngine.prototype, "loadingScreen", _MissingSideEffectProperty("AbstractEngine", "loadingScreen")); -} -if (!Object.getOwnPropertyDescriptor(AbstractEngine.prototype, "loadingUIText")) { - Object.defineProperty(AbstractEngine.prototype, "loadingUIText", _MissingSideEffectProperty("AbstractEngine", "loadingUIText")); -} -if (!Object.getOwnPropertyDescriptor(AbstractEngine.prototype, "loadingUIBackgroundColor")) { - Object.defineProperty(AbstractEngine.prototype, "loadingUIBackgroundColor", _MissingSideEffectProperty("AbstractEngine", "loadingUIBackgroundColor")); -} -if (!Object.getOwnPropertyDescriptor(AbstractEngine.prototype, "inputElement")) { - Object.defineProperty(AbstractEngine.prototype, "inputElement", _MissingSideEffectProperty("AbstractEngine", "inputElement")); -} -if (!Object.getOwnPropertyDescriptor(AbstractEngine.prototype, "activeView")) { - Object.defineProperty(AbstractEngine.prototype, "activeView", _MissingSideEffectProperty("AbstractEngine", "activeView")); -} -if (!Object.getOwnPropertyDescriptor(AbstractEngine.prototype, "views")) { - Object.defineProperty(AbstractEngine.prototype, "views", _MissingSideEffectProperty("AbstractEngine", "views")); -} -// #endregion GENERATED_SIDE_EFFECT_STUBS diff --git a/packages/dev/core/src/Engines/engine.pure.ts b/packages/dev/core/src/Engines/engine.pure.ts index 91af8e8df6d8..ef544134f04e 100644 --- a/packages/dev/core/src/Engines/engine.pure.ts +++ b/packages/dev/core/src/Engines/engine.pure.ts @@ -1067,19 +1067,3 @@ export class Engine extends ThinEngine { super.dispose(); } } - -// #region GENERATED_SIDE_EFFECT_STUBS — do not edit, regenerate with `npm run generate:side-effect-stubs` -import { _MissingSideEffect } from "../Misc/devTools"; - -Engine.prototype.createMultiviewRenderTargetTexture ??= _MissingSideEffect("Engine", "createMultiviewRenderTargetTexture") as any; -Engine.prototype.bindMultiviewFramebuffer ??= _MissingSideEffect("Engine", "bindMultiviewFramebuffer") as any; -Engine.prototype.bindSpaceWarpFramebuffer ??= _MissingSideEffect("Engine", "bindSpaceWarpFramebuffer") as any; -Engine.prototype.createTransformFeedback ??= _MissingSideEffect("Engine", "createTransformFeedback") as any; -Engine.prototype.deleteTransformFeedback ??= _MissingSideEffect("Engine", "deleteTransformFeedback") as any; -Engine.prototype.bindTransformFeedback ??= _MissingSideEffect("Engine", "bindTransformFeedback") as any; -Engine.prototype.beginTransformFeedback ??= _MissingSideEffect("Engine", "beginTransformFeedback") as any; -Engine.prototype.endTransformFeedback ??= _MissingSideEffect("Engine", "endTransformFeedback") as any; -Engine.prototype.setTranformFeedbackVaryings ??= _MissingSideEffect("Engine", "setTranformFeedbackVaryings") as any; -Engine.prototype.bindTransformFeedbackBuffer ??= _MissingSideEffect("Engine", "bindTransformFeedbackBuffer") as any; -Engine.prototype.readTransformFeedbackBuffer ??= _MissingSideEffect("Engine", "readTransformFeedbackBuffer") as any; -// #endregion GENERATED_SIDE_EFFECT_STUBS diff --git a/packages/dev/core/src/Engines/thinEngine.pure.ts b/packages/dev/core/src/Engines/thinEngine.pure.ts index 75dbd515ec75..5983d1f860b1 100644 --- a/packages/dev/core/src/Engines/thinEngine.pure.ts +++ b/packages/dev/core/src/Engines/thinEngine.pure.ts @@ -4705,16 +4705,3 @@ interface TexImageParameters { format: number; type: number; } - -// #region GENERATED_SIDE_EFFECT_STUBS — do not edit, regenerate with `npm run generate:side-effect-stubs` -import { _MissingSideEffect } from "../Misc/devTools"; - -ThinEngine.prototype.startTimeQuery ??= _MissingSideEffect("ThinEngine", "startTimeQuery") as any; -ThinEngine.prototype.endTimeQuery ??= _MissingSideEffect("ThinEngine", "endTimeQuery") as any; -ThinEngine.prototype.createUniformBuffer ??= _MissingSideEffect("ThinEngine", "createUniformBuffer") as any; -ThinEngine.prototype.createDynamicUniformBuffer ??= _MissingSideEffect("ThinEngine", "createDynamicUniformBuffer") as any; -ThinEngine.prototype.updateUniformBuffer ??= _MissingSideEffect("ThinEngine", "updateUniformBuffer") as any; -ThinEngine.prototype.bindUniformBuffer ??= _MissingSideEffect("ThinEngine", "bindUniformBuffer") as any; -ThinEngine.prototype.bindUniformBufferBase ??= _MissingSideEffect("ThinEngine", "bindUniformBufferBase") as any; -ThinEngine.prototype.bindUniformBlock ??= _MissingSideEffect("ThinEngine", "bindUniformBlock") as any; -// #endregion GENERATED_SIDE_EFFECT_STUBS diff --git a/packages/dev/core/src/Materials/effect.pure.ts b/packages/dev/core/src/Materials/effect.pure.ts index 5f7cc1374f1c..fe721c8ba5c6 100644 --- a/packages/dev/core/src/Materials/effect.pure.ts +++ b/packages/dev/core/src/Materials/effect.pure.ts @@ -1584,11 +1584,3 @@ export class Effect implements IDisposable { Effect._BaseCache = {}; } } - -// #region GENERATED_SIDE_EFFECT_STUBS — do not edit, regenerate with `npm run generate:side-effect-stubs` -import { _MissingSideEffect } from "../Misc/devTools"; - -Effect.prototype.setDepthStencilTexture ??= _MissingSideEffect("Effect", "setDepthStencilTexture") as any; -Effect.prototype.setTextureFromPostProcess ??= _MissingSideEffect("Effect", "setTextureFromPostProcess") as any; -Effect.prototype.setTextureFromPostProcessOutput ??= _MissingSideEffect("Effect", "setTextureFromPostProcessOutput") as any; -// #endregion GENERATED_SIDE_EFFECT_STUBS diff --git a/packages/dev/core/src/Misc/fileTools.pure.ts b/packages/dev/core/src/Misc/fileTools.pure.ts index 55b8e31bd62f..69ab5ecaedb4 100644 --- a/packages/dev/core/src/Misc/fileTools.pure.ts +++ b/packages/dev/core/src/Misc/fileTools.pure.ts @@ -11,6 +11,8 @@ import { RetryStrategy } from "./retryStrategy"; import { BaseError, ErrorCodes, RuntimeError } from "./error"; import { DecodeBase64ToBinary, DecodeBase64ToString, EncodeArrayBufferToBase64 } from "./stringTools"; import { EngineStore } from "../Engines/engineStore"; +import { EngineFunctionContext } from "../Engines/abstractEngine.functions"; +import { _FunctionContainer } from "../Engines/Processors/shaderProcessor"; import { Logger } from "./logger"; import { TimingTools } from "./timingTools"; import { GetBlobBufferSource } from "../Buffers/bufferUtils"; @@ -1049,4 +1051,11 @@ export function RegisterFileTools(): void { RequestFile, SetCorsBehavior ); + + // Register the file/image loaders on the engine injection seam so the + // always-loaded engine classes can load files/images without importing + // fileTools directly (which would defeat tree-shaking). + EngineFunctionContext.loadFile = LoadFile; + EngineFunctionContext.loadImage = LoadImage; + _FunctionContainer.loadFile = LoadFile; } diff --git a/packages/dev/core/src/Misc/observable.pure.ts b/packages/dev/core/src/Misc/observable.pure.ts index 5dd147d5861d..a9ecebfce71e 100644 --- a/packages/dev/core/src/Misc/observable.pure.ts +++ b/packages/dev/core/src/Misc/observable.pure.ts @@ -532,11 +532,3 @@ export class Observable implements IReadonlyObservable { return false; } } - -// #region GENERATED_SIDE_EFFECT_STUBS — do not edit, regenerate with `npm run generate:side-effect-stubs` -import { _MissingSideEffect } from "./devTools"; - -Observable.prototype.notifyObserversWithPromise ??= _MissingSideEffect("Observable", "notifyObserversWithPromise") as any; -Observable.prototype.runCoroutineAsync ??= _MissingSideEffect("Observable", "runCoroutineAsync") as any; -Observable.prototype.cancelAllCoroutines ??= _MissingSideEffect("Observable", "cancelAllCoroutines") as any; -// #endregion GENERATED_SIDE_EFFECT_STUBS diff --git a/packages/tools/tests/scripts/generateFileSizes.js b/packages/tools/tests/scripts/generateFileSizes.js index b7a1776b6828..03127ae8a78c 100644 --- a/packages/tools/tests/scripts/generateFileSizes.js +++ b/packages/tools/tests/scripts/generateFileSizes.js @@ -10,6 +10,15 @@ for (const key in flags) { process.env[key] = flags[key]; } +// Absolute per-case growth caps (in bytes) applied in addition to the +// percentage thresholds above. Some cases must stay minimal regardless of +// their percentage headroom, so any single PR that grows them past the cap +// fails the build. The thin engine is the strictest: it may not grow by more +// than 2 KB in a single PR. +const absoluteGrowthCaps = { + thinEngineOnly: 2000, +}; + const sizes = {}; glob.globSync("./dist/*").forEach((file) => { // ignore files, only operate on directories @@ -71,15 +80,22 @@ https.get("https://cdn.babylonjs.com/fileSizes.json", (res) => { loadedAsyncSize = fileSizes[filename].async; } if (loadedMainSize < currentMainSize) { - // check if increase is more than 10% const errorThreshold = Number.parseFloat(process.env.errorThreshold || "1.1"); const warningThreshold = Number.parseFloat(process.env.warningThreshold || "1.05"); - if (currentMainSize > fileSizes[filename] * errorThreshold) { + const growth = currentMainSize - loadedMainSize; + const absoluteCap = absoluteGrowthCaps[caseName]; + if (absoluteCap !== undefined && growth > absoluteCap) { + // This case has a hard per-PR byte budget that is stricter than the percentage thresholds. + console.log( + `##[error] File size for ${filename} has increased from ${loadedMainSize} to ${currentMainSize} - grew ${growth} bytes, more than the ${absoluteCap} byte cap for this case` + ); + error = true; + } else if (currentMainSize > loadedMainSize * errorThreshold) { console.log( `##[error] File size for ${filename} has increased from ${loadedMainSize} to ${currentMainSize} - more than ${Math.floor((errorThreshold - 1) * 100)}%` ); error = true; - } else if (currentMainSize > fileSizes[filename] * warningThreshold) { + } else if (currentMainSize > loadedMainSize * warningThreshold) { console.log( `##[warn] File size for ${filename} has increased from ${loadedMainSize} to ${currentMainSize} - more than ${Math.floor((warningThreshold - 1) * 100)}%` ); diff --git a/scripts/treeshaking/generateSideEffectStubs.mjs b/scripts/treeshaking/generateSideEffectStubs.mjs index 0ea9134aa618..fe06610c916c 100644 --- a/scripts/treeshaking/generateSideEffectStubs.mjs +++ b/scripts/treeshaking/generateSideEffectStubs.mjs @@ -14,7 +14,7 @@ */ import { readFileSync, writeFileSync, existsSync } from "fs"; -import { resolve, dirname, relative, posix } from "path"; +import { resolve, dirname, relative, posix, join } from "path"; import { globSync } from "glob"; import { execFileSync } from "child_process"; import { getPackageConfig, resolvePackageFromArgv } from "./packageConfig.mjs"; @@ -35,11 +35,83 @@ const REGION_START = "// #region GENERATED_SIDE_EFFECT_STUBS — do not edit, re const REGION_END = "// #endregion GENERATED_SIDE_EFFECT_STUBS"; const PRETTIER_PRINT_WIDTH = 180; +// Classes that are part of the always-loaded engine hierarchy. These base +// classes are pulled into every build that creates an engine, so any stub +// attached to them can never be tree-shaken and inflates even the most minimal +// (thin-engine-only) bundle. We deliberately skip generating side-effect stubs +// for them: the small loss of a friendly "requires a side-effect import" +// warning on these specific augmented methods is worth keeping the core engine +// footprint minimal. Callers of an unimported augmented method on these classes +// still get a standard "x is not a function" TypeError. +// +// The thin-engine closure below (STUB_EXCLUDED_FILES) already covers the WebGL +// thin engine (AbstractEngine, ThinEngine). This list additionally covers the +// heavier engine variants (Engine, WebGPU, Native) whose classes are not part +// of the minimal thin-engine closure but are still always-loaded once you use +// that engine — keeping their augmented-method stubs out of every build too. +const STUB_EXCLUDED_CLASSES = new Set(["AbstractEngine", "ThinEngine", "Engine", "WebGPUEngine", "ThinWebGPUEngine", "NativeEngine", "ThinNativeEngine"]); + +/** + * Absolute paths of files that make up the thin-engine import closure. + * + * Every file pulled in by `new ThinEngine(canvas)` (see + * packages/tools/tests/src/thinEngineOnly.ts) is loaded in even the most + * minimal engine build, so a module-load stub (`X.prototype.m ??= ...`) placed + * in one of them can never be tree-shaken and permanently inflates the thin + * engine. The authoritative list lives in `thin-engine-modules.json` (derived + * from the real production bundle) rather than being recomputed here: a + * source-only esbuild pass tree-shakes differently from the shipped build + * (constants inlining, injected `/*#__PURE__*\/` annotations) and would + * under-count the true closure. Populated by `loadThinEngineClosure()` below; + * only meaningful for the core package. + * @type {Set} + */ +let STUB_EXCLUDED_FILES; + +/** + * Load the authoritative thin-engine closure from `thin-engine-modules.json` and + * resolve each module to an absolute source path. Only relevant for `core` (the + * thin engine lives there and never imports the other packages). Every listed + * module must exist so a rename can never silently drop a file out of the + * exclusion set (which would let a stub creep back into the thin engine). + * @returns {Set} absolute file paths in the thin-engine closure + */ +function loadThinEngineClosure() { + if (PACKAGE !== "core") { + return new Set(); + } + const coreSrc = join(REPO_ROOT, "packages", "dev", "core", "src"); + const listPath = join(REPO_ROOT, "scripts", "treeshaking", "thin-engine-modules.json"); + const { modules } = JSON.parse(readFileSync(listPath, "utf8")); + const files = new Set(); + const missing = []; + for (const rel of modules) { + const abs = join(coreSrc, rel); + if (!existsSync(abs)) { + missing.push(rel); + continue; + } + files.add(abs); + } + if (missing.length > 0) { + throw new Error( + `Stale thin-engine exclusion list: ${missing.length} module(s) in thin-engine-modules.json no longer exist ` + + `(${missing.join(", ")}). Update scripts/treeshaking/thin-engine-modules.json after the rename/removal.` + ); + } + return files; +} + // ── Step 1: Find all .types.ts files ──────────────────────────────────────── const typesFiles = globSync("**/*.types.ts", { cwd: ROOT, absolute: true }).sort(); if (VERBOSE) console.log(`Found ${typesFiles.length} .types.ts files`); +// Load the thin-engine closure up front so stubs are never emitted into any +// always-loaded thin-engine file (see STUB_EXCLUDED_FILES). +STUB_EXCLUDED_FILES = loadThinEngineClosure(); +if (VERBOSE) console.log(`Thin-engine closure: ${STUB_EXCLUDED_FILES.size} files excluded from stub generation`); + // ── Step 2: Parse declare module blocks ───────────────────────────────────── /** @@ -267,6 +339,18 @@ for (const typesFile of typesFiles) { continue; } + // Always-loaded files are excluded from stub generation so their stubs + // don't inflate minimal bundles. A file is excluded when it is part of + // the thin-engine closure (STUB_EXCLUDED_FILES) or when the augmented + // class is a known always-loaded engine variant (STUB_EXCLUDED_CLASSES). + // We still register the target file below so any previously generated + // region gets stripped, but we emit no members. + const classExcluded = STUB_EXCLUDED_CLASSES.has(block.interfaceName) || STUB_EXCLUDED_FILES.has(targetFile); + if (classExcluded && VERBOSE) { + const reason = STUB_EXCLUDED_FILES.has(targetFile) ? "in thin-engine closure" : "always-loaded engine class"; + console.log(` SKIP: ${block.interfaceName} (${reason}) — excluded from stub generation`); + } + if (!stubsByFile.has(targetFile)) { stubsByFile.set(targetFile, new Map()); } @@ -276,6 +360,10 @@ for (const typesFile of typesFiles) { } const stubs = classMap.get(block.interfaceName); + if (classExcluded) { + continue; + } + for (const member of block.members) { if (member.isInternal) { totalSkipped++; @@ -399,6 +487,7 @@ for (const [targetFile, classMap] of stubsByFile) { // Read target file first so we can detect its line endings let content = readFileSync(targetFile, "utf-8"); + const originalContent = content; const eol = content.includes("\r\n") ? "\r\n" : "\n"; // Strip existing stub region before searching for imports (so we don't match @@ -411,6 +500,11 @@ for (const [targetFile, classMap] of stubsByFile) { if (content[endSkip] === "\r") endSkip++; if (content[endSkip] === "\n") endSkip++; contentWithoutRegion = content.slice(0, regionStartIdx) + content.slice(endSkip); + // Removing a region that was appended at EOF leaves the blank separator + // line that preceded it, producing a trailing blank line. Collapse any + // trailing whitespace down to a single end-of-line so the result stays + // prettier-clean (and `--check` matches a fresh regeneration). + contentWithoutRegion = contentWithoutRegion.replace(/\s*$/, eol); } // Determine which devTools symbols this file's stubs need @@ -430,15 +524,27 @@ for (const [targetFile, classMap] of stubsByFile) { let stubImportLine = ""; if (existingImportMatch) { - // Merge: parse existing symbols and add missing ones + // Merge: parse existing symbols, drop any generator-managed symbols that + // are no longer needed (e.g. when a class becomes excluded or loses all + // its stubs), then add the currently-needed ones. This keeps the external + // devTools import free of dangling unused symbols so `--check` stays + // stable and lint doesn't flag orphaned imports. + const managedSymbols = new Set(["_MissingSideEffect", "_MissingSideEffectProperty"]); const existingSymbols = existingImportMatch[1] .split(",") .map((s) => s.trim()) .filter(Boolean); - const allSymbols = [...new Set([...existingSymbols, ...neededSymbols])]; - const newImport = `import { ${allSymbols.join(", ")} } from "${relPath}";`; - // Replace in the original content (not contentWithoutRegion) - content = content.replace(existingImportMatch[0], newImport); + const keptSymbols = existingSymbols.filter((s) => !managedSymbols.has(s) || neededSymbols.includes(s)); + const allSymbols = [...new Set([...keptSymbols, ...neededSymbols])]; + // If nothing remains, drop the import line entirely; otherwise rewrite it. + const newImport = allSymbols.length > 0 ? `import { ${allSymbols.join(", ")} } from "${relPath}";` : ""; + const replacement = newImport || ""; + if (newImport !== existingImportMatch[0]) { + content = content.replace(existingImportMatch[0], replacement); + // Keep contentWithoutRegion in sync so the stale-removal path below + // writes the cleaned import rather than the original merged one. + contentWithoutRegion = contentWithoutRegion.replace(existingImportMatch[0], replacement); + } } else { // No existing import — add import line inside the stub block stubImportLine = `import { ${neededSymbols.join(", ")} } from "${relPath}";`; @@ -472,7 +578,7 @@ for (const [targetFile, classMap] of stubsByFile) { lines.push(REGION_END); if (fileStubCount === 0) { - if (contentWithoutRegion !== content) { + if (contentWithoutRegion !== originalContent) { if (DRY_RUN) { console.log(`[DRY RUN] Would remove stale stubs from ${relative(ROOT, targetFile)}`); } else if (CHECK) { diff --git a/scripts/treeshaking/thin-engine-modules.json b/scripts/treeshaking/thin-engine-modules.json new file mode 100644 index 000000000000..be93c82b8d93 --- /dev/null +++ b/scripts/treeshaking/thin-engine-modules.json @@ -0,0 +1,70 @@ +{ + "_comment": [ + "Authoritative list of @babylonjs/core source modules that make up the thin-engine", + "import closure (everything pulled in by `new ThinEngine(canvas)` — see", + "packages/tools/tests/src/thinEngineOnly.ts). These files are ALWAYS loaded in even", + "the most minimal engine build, so any side-effect stub placed in one of them can", + "never be tree-shaken and permanently inflates the thin-engine bundle.", + "generateSideEffectStubs.mjs excludes every file listed here from stub generation.", + "", + "To refresh this list: build @babylonjs/core (npm run build) so constants are inlined,", + "then bundle packages/tools/tests/src/thinEngineOnly.ts with esbuild --bundle --minify", + "--metafile and take the core module inputs. Paths are relative to packages/dev/core/src", + "and use the SOURCE (.ts / .pure.ts) form of each built .js file." + ], + "modules": [ + "Buffers/buffer.pure.ts", + "Buffers/bufferUtils.ts", + "Buffers/dataBuffer.ts", + "Engines/Extensions/engine.dynamicBuffer.pure.ts", + "Engines/Processors/Expressions/Operators/shaderDefineAndOperator.ts", + "Engines/Processors/Expressions/Operators/shaderDefineArithmeticOperator.ts", + "Engines/Processors/Expressions/Operators/shaderDefineIsDefinedOperator.ts", + "Engines/Processors/Expressions/Operators/shaderDefineOrOperator.ts", + "Engines/Processors/Expressions/shaderDefineExpression.ts", + "Engines/Processors/shaderCodeConditionNode.ts", + "Engines/Processors/shaderCodeCursor.ts", + "Engines/Processors/shaderCodeNode.ts", + "Engines/Processors/shaderCodeTestNode.ts", + "Engines/Processors/shaderProcessor.ts", + "Engines/WebGL/webGL2ShaderProcessors.ts", + "Engines/WebGL/webGLHardwareTexture.ts", + "Engines/WebGL/webGLPipelineContext.ts", + "Engines/WebGL/webGLShaderProcessors.ts", + "Engines/abstractEngine.functions.ts", + "Engines/abstractEngine.pure.ts", + "Engines/engineStore.ts", + "Engines/performanceConfigurator.ts", + "Engines/shaderStore.ts", + "Engines/thinEngine.functions.ts", + "Engines/thinEngine.pure.ts", + "Materials/Textures/internalTexture.ts", + "Materials/Textures/textureHelper.functions.ts", + "Materials/Textures/textureSampler.ts", + "Materials/Textures/thinRenderTargetTexture.ts", + "Materials/Textures/thinTexture.ts", + "Materials/drawWrapper.functions.ts", + "Materials/drawWrapper.ts", + "Materials/effect.functions.ts", + "Materials/effect.pure.ts", + "Materials/effectRenderer.pure.ts", + "Maths/math.size.ts", + "Maths/math.viewport.ts", + "Meshes/WebGL/webGLDataBuffer.ts", + "Misc/devTools.ts", + "Misc/domManagement.ts", + "Misc/halfFloat.ts", + "Misc/logger.ts", + "Misc/observable.pure.ts", + "Misc/observable.ts", + "Misc/precisionDate.ts", + "Misc/timingTools.ts", + "Misc/tools.functions.ts", + "Shaders/postprocess.vertex.ts", + "ShadersWGSL/postprocess.vertex.ts", + "States/alphaCullingState.ts", + "States/depthCullingState.ts", + "States/stencilState.ts", + "States/stencilStateComposer.ts" + ] +}