Skip to content

Commit 2ef904c

Browse files
authored
Asynchronous shader compilation in Babylon Native (#13587)
1 parent 1a9a65e commit 2ef904c

File tree

3 files changed

+64
-15
lines changed

3 files changed

+64
-15
lines changed

packages/dev/core/src/Engines/Native/nativeInterfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export interface INativeEngine {
3939
updateDynamicVertexBuffer(vertexBuffer: NativeData, bytes: ArrayBuffer, byteOffset: number, byteLength: number): void;
4040

4141
createProgram(vertexShader: string, fragmentShader: string): NativeProgram;
42+
createProgramAsync(vertexShader: string, fragmentShader: string, onSuccess: () => void, onError: (error: Error) => void): NativeProgram;
4243
getUniforms(shaderProgram: NativeProgram, uniformsNames: string[]): WebGLUniformLocation[];
4344
getAttributes(shaderProgram: NativeProgram, attributeNames: string[]): number[];
4445

packages/dev/core/src/Engines/Native/nativePipelineContext.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,23 @@ import type { IPipelineContext } from "../IPipelineContext";
55
import type { NativeEngine } from "../nativeEngine";
66

77
export class NativePipelineContext implements IPipelineContext {
8-
// TODO: async should be true?
9-
public isAsync = false;
10-
public isReady = false;
8+
public isParallelCompiled: boolean = true;
9+
public isCompiled: boolean = false;
10+
public compilationError?: Error;
11+
12+
public get isAsync(): boolean {
13+
return this.isParallelCompiled;
14+
}
15+
16+
public get isReady(): boolean {
17+
if (this.compilationError) {
18+
const message = this.compilationError.message;
19+
throw new Error("SHADER ERROR" + (typeof message === "string" ? "\n" + message : ""));
20+
}
21+
return this.isCompiled;
22+
}
23+
24+
public onCompiled?: () => void;
1125

1226
public _getVertexShaderCode(): string | null {
1327
return null;

packages/dev/core/src/Engines/nativeEngine.ts

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -646,27 +646,44 @@ export class NativeEngine extends Engine {
646646
}
647647
}
648648

649-
/**
650-
* @internal
651-
*/
652-
public _isRenderingStateCompiled(pipelineContext: IPipelineContext): boolean {
653-
// TODO: support async shader compilcation
654-
return true;
649+
public isAsync(pipelineContext: IPipelineContext): boolean {
650+
return !!(pipelineContext.isAsync && this._engine.createProgramAsync);
655651
}
656652

657653
/**
658654
* @internal
659655
*/
660656
public _executeWhenRenderingStateIsCompiled(pipelineContext: IPipelineContext, action: () => void) {
661-
// TODO: support async shader compilcation
662-
action();
657+
const nativePipelineContext = pipelineContext as NativePipelineContext;
658+
659+
if (!this.isAsync(pipelineContext)) {
660+
action();
661+
return;
662+
}
663+
664+
const oldHandler = nativePipelineContext.onCompiled;
665+
666+
if (oldHandler) {
667+
nativePipelineContext.onCompiled = () => {
668+
oldHandler!();
669+
action();
670+
};
671+
} else {
672+
nativePipelineContext.onCompiled = action;
673+
}
663674
}
664675

665676
public createRawShaderProgram(): WebGLProgram {
666677
throw new Error("Not Supported");
667678
}
668679

669-
public createShaderProgram(_pipelineContext: IPipelineContext, vertexCode: string, fragmentCode: string, defines: Nullable<string>): WebGLProgram {
680+
public createShaderProgram(pipelineContext: IPipelineContext, vertexCode: string, fragmentCode: string, defines: Nullable<string>): WebGLProgram {
681+
const nativePipelineContext = pipelineContext as NativePipelineContext;
682+
683+
if (nativePipelineContext.nativeProgram) {
684+
throw new Error("Tried to create a second program in the same NativePipelineContext");
685+
}
686+
670687
this.onBeforeShaderCompilationObservable.notifyObservers(this);
671688

672689
const vertexInliner = new ShaderCodeInliner(vertexCode);
@@ -680,9 +697,26 @@ export class NativeEngine extends Engine {
680697
vertexCode = ThinEngine._ConcatenateShader(vertexCode, defines);
681698
fragmentCode = ThinEngine._ConcatenateShader(fragmentCode, defines);
682699

683-
const program = this._engine.createProgram(vertexCode, fragmentCode);
684-
this.onAfterShaderCompilationObservable.notifyObservers(this);
685-
return program as WebGLProgram;
700+
const onSuccess = () => {
701+
nativePipelineContext.isCompiled = true;
702+
nativePipelineContext.onCompiled?.();
703+
this.onAfterShaderCompilationObservable.notifyObservers(this);
704+
};
705+
706+
if (this.isAsync(pipelineContext)) {
707+
return this._engine.createProgramAsync(vertexCode, fragmentCode, onSuccess, (error: Error) => {
708+
nativePipelineContext.compilationError = error;
709+
}) as WebGLProgram;
710+
} else {
711+
try {
712+
const program = (nativePipelineContext.nativeProgram = this._engine.createProgram(vertexCode, fragmentCode));
713+
onSuccess();
714+
return program as WebGLProgram;
715+
} catch (e: any) {
716+
const message = e?.message;
717+
throw new Error("SHADER ERROR" + (typeof message === "string" ? "\n" + message : ""));
718+
}
719+
}
686720
}
687721

688722
/**

0 commit comments

Comments
 (0)