diff --git a/packages/dev/core/src/Engines/Native/nativeInterfaces.ts b/packages/dev/core/src/Engines/Native/nativeInterfaces.ts index 0cd28fa1aea..5564824c378 100644 --- a/packages/dev/core/src/Engines/Native/nativeInterfaces.ts +++ b/packages/dev/core/src/Engines/Native/nativeInterfaces.ts @@ -39,6 +39,7 @@ export interface INativeEngine { updateDynamicVertexBuffer(vertexBuffer: NativeData, bytes: ArrayBuffer, byteOffset: number, byteLength: number): void; createProgram(vertexShader: string, fragmentShader: string): NativeProgram; + createProgramAsync(vertexShader: string, fragmentShader: string, onSuccess: () => void, onError: (error: Error) => void): NativeProgram; getUniforms(shaderProgram: NativeProgram, uniformsNames: string[]): WebGLUniformLocation[]; getAttributes(shaderProgram: NativeProgram, attributeNames: string[]): number[]; diff --git a/packages/dev/core/src/Engines/Native/nativePipelineContext.ts b/packages/dev/core/src/Engines/Native/nativePipelineContext.ts index 28691b8b464..7d6ec09fbfa 100644 --- a/packages/dev/core/src/Engines/Native/nativePipelineContext.ts +++ b/packages/dev/core/src/Engines/Native/nativePipelineContext.ts @@ -5,9 +5,23 @@ import type { IPipelineContext } from "../IPipelineContext"; import type { NativeEngine } from "../nativeEngine"; export class NativePipelineContext implements IPipelineContext { - // TODO: async should be true? - public isAsync = false; - public isReady = false; + public isParallelCompiled: boolean = true; + public isCompiled: boolean = false; + public compilationError?: Error; + + public get isAsync(): boolean { + return this.isParallelCompiled; + } + + public get isReady(): boolean { + if (this.compilationError) { + const message = this.compilationError.message; + throw new Error("SHADER ERROR" + (typeof message === "string" ? "\n" + message : "")); + } + return this.isCompiled; + } + + public onCompiled?: () => void; public _getVertexShaderCode(): string | null { return null; diff --git a/packages/dev/core/src/Engines/nativeEngine.ts b/packages/dev/core/src/Engines/nativeEngine.ts index cd90d793e80..2e33e4b40e3 100644 --- a/packages/dev/core/src/Engines/nativeEngine.ts +++ b/packages/dev/core/src/Engines/nativeEngine.ts @@ -646,27 +646,44 @@ export class NativeEngine extends Engine { } } - /** - * @internal - */ - public _isRenderingStateCompiled(pipelineContext: IPipelineContext): boolean { - // TODO: support async shader compilcation - return true; + public isAsync(pipelineContext: IPipelineContext): boolean { + return !!(pipelineContext.isAsync && this._engine.createProgramAsync); } /** * @internal */ public _executeWhenRenderingStateIsCompiled(pipelineContext: IPipelineContext, action: () => void) { - // TODO: support async shader compilcation - action(); + const nativePipelineContext = pipelineContext as NativePipelineContext; + + if (!this.isAsync(pipelineContext)) { + action(); + return; + } + + const oldHandler = nativePipelineContext.onCompiled; + + if (oldHandler) { + nativePipelineContext.onCompiled = () => { + oldHandler!(); + action(); + }; + } else { + nativePipelineContext.onCompiled = action; + } } public createRawShaderProgram(): WebGLProgram { throw new Error("Not Supported"); } - public createShaderProgram(_pipelineContext: IPipelineContext, vertexCode: string, fragmentCode: string, defines: Nullable): WebGLProgram { + public createShaderProgram(pipelineContext: IPipelineContext, vertexCode: string, fragmentCode: string, defines: Nullable): WebGLProgram { + const nativePipelineContext = pipelineContext as NativePipelineContext; + + if (nativePipelineContext.nativeProgram) { + throw new Error("Tried to create a second program in the same NativePipelineContext"); + } + this.onBeforeShaderCompilationObservable.notifyObservers(this); const vertexInliner = new ShaderCodeInliner(vertexCode); @@ -680,9 +697,26 @@ export class NativeEngine extends Engine { vertexCode = ThinEngine._ConcatenateShader(vertexCode, defines); fragmentCode = ThinEngine._ConcatenateShader(fragmentCode, defines); - const program = this._engine.createProgram(vertexCode, fragmentCode); - this.onAfterShaderCompilationObservable.notifyObservers(this); - return program as WebGLProgram; + const onSuccess = () => { + nativePipelineContext.isCompiled = true; + nativePipelineContext.onCompiled?.(); + this.onAfterShaderCompilationObservable.notifyObservers(this); + }; + + if (this.isAsync(pipelineContext)) { + return this._engine.createProgramAsync(vertexCode, fragmentCode, onSuccess, (error: Error) => { + nativePipelineContext.compilationError = error; + }) as WebGLProgram; + } else { + try { + const program = (nativePipelineContext.nativeProgram = this._engine.createProgram(vertexCode, fragmentCode)); + onSuccess(); + return program as WebGLProgram; + } catch (e: any) { + const message = e?.message; + throw new Error("SHADER ERROR" + (typeof message === "string" ? "\n" + message : "")); + } + } } /**