Skip to content

Commit

Permalink
HighlightLayer webgpu port (BabylonJS#15375)
Browse files Browse the repository at this point in the history
* Port Highlight Layer to webgpu

* Fix race condition
  • Loading branch information
deltakosh authored Aug 7, 2024
1 parent a8a55bb commit 8749f61
Show file tree
Hide file tree
Showing 17 changed files with 620 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ WebGPUEngine.prototype.createRawCubeTextureFromUrl = function (
const texture = this.createRawCubeTexture(null, size, format, type, !noMipmap, invertY, samplingMode, null);
scene?.addPendingData(texture);
texture.url = url;
texture.isReady = false;

this._internalTexturesCache.push(texture);

Expand Down
45 changes: 43 additions & 2 deletions packages/dev/core/src/Layers/effectLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { DrawWrapper } from "../Materials/drawWrapper";
import { addClipPlaneUniforms, bindClipPlane, prepareStringDefinesForClipPlanes } from "../Materials/clipPlaneMaterialHelper";
import { BindMorphTargetParameters, PrepareAttributesForMorphTargetsInfluencers, PushAttributesForInstances } from "../Materials/materialHelper.functions";
import { GetExponentOfTwo } from "../Misc/tools.functions";
import { ShaderLanguage } from "core/Materials/shaderLanguage";

/**
* Effect layer options. This helps customizing the behaviour
Expand Down Expand Up @@ -98,6 +99,12 @@ export abstract class EffectLayer {
protected _emissiveTextureAndColor: { texture: Nullable<BaseTexture>; color: Color4 } = { texture: null, color: new Color4() };
protected _effectIntensity: { [meshUniqueId: number]: number } = {};

/**
* Force all the effect layers to compile to glsl even on WebGPU engines.
* False by default. This is mostly meant for backward compatibility.
*/
public static ForceGLSL = false;

/**
* The name of the layer
*/
Expand Down Expand Up @@ -183,6 +190,16 @@ export abstract class EffectLayer {
return this._mainTexture;
}

/** Shader language used by the material */
protected _shaderLanguage = ShaderLanguage.GLSL;

/**
* Gets the shader language used in this material.
*/
public get shaderLanguage(): ShaderLanguage {
return this._shaderLanguage;
}

/**
* @internal
*/
Expand Down Expand Up @@ -239,17 +256,21 @@ export abstract class EffectLayer {
* Instantiates a new effect Layer and references it in the scene.
* @param name The name of the layer
* @param scene The scene to use the layer in
* @param forceGLSL Use the GLSL code generation for the shader (even on WebGPU). Default is false
*/
constructor(
/** The Friendly of the effect in the scene */
name: string,
scene?: Scene
scene?: Scene,
forceGLSL = false
) {
this.name = name;

this._scene = scene || <Scene>EngineStore.LastCreatedScene;
EffectLayer._SceneComponentInitialization(this._scene);

this._initShaderSourceAsync(forceGLSL);

this._engine = this._scene.getEngine();
this._maxSize = this._engine.getCaps().maxTextureSize;
this._scene.effectLayers.push(this);
Expand All @@ -261,6 +282,21 @@ export abstract class EffectLayer {
this._generateVertexBuffer();
}

private _shadersLoaded = false;
protected async _initShaderSourceAsync(forceGLSL = false) {
const engine = this._scene.getEngine();

if (engine.isWebGPU && !forceGLSL && !EffectLayer.ForceGLSL) {
this._shaderLanguage = ShaderLanguage.WGSL;

await Promise.all([import("../ShadersWGSL/glowMapGeneration.fragment"), import("../ShadersWGSL/glowMapGeneration.vertex")]);
} else {
await Promise.all([import("../Shaders/glowMapGeneration.fragment"), import("../Shaders/glowMapGeneration.vertex")]);
}

this._shadersLoaded = true;
}

/**
* Get the effect name of the layer.
* @returns The effect name
Expand Down Expand Up @@ -542,6 +578,10 @@ export abstract class EffectLayer {
* @returns true if ready otherwise, false
*/
protected _isReady(subMesh: SubMesh, useInstances: boolean, emissiveTexture: Nullable<BaseTexture>): boolean {
if (!this._shadersLoaded) {
return false;
}

const engine = this._scene.getEngine();
const mesh = subMesh.getMesh();

Expand Down Expand Up @@ -727,7 +767,8 @@ export abstract class EffectLayer {
fallbacks,
undefined,
undefined,
{ maxSimultaneousMorphTargets: morphInfluencers }
{ maxSimultaneousMorphTargets: morphInfluencers },
this._shaderLanguage
),
join
);
Expand Down
33 changes: 30 additions & 3 deletions packages/dev/core/src/Layers/glowLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,10 @@ import { RegisterClass } from "../Misc/typeStore";
import { Color4 } from "../Maths/math.color";
import type { PBRMaterial } from "../Materials/PBR/pbrMaterial";

import "../Shaders/glowMapMerge.fragment";
import "../Shaders/glowMapMerge.vertex";
import "../Layers/effectLayerSceneComponent";
import { SerializationHelper } from "../Misc/decorators.serialization";
import { GetExponentOfTwo } from "../Misc/tools.functions";
import { ShaderLanguage } from "core/Materials/shaderLanguage";

declare module "../abstractScene" {
export interface AbstractScene {
Expand Down Expand Up @@ -231,6 +230,23 @@ export class GlowLayer extends EffectLayer {
});
}

protected override async _initShaderSourceAsync(forceGLSL = false) {
const engine = this._scene.getEngine();

if (engine.isWebGPU && !forceGLSL && !EffectLayer.ForceGLSL) {
this._shaderLanguage = ShaderLanguage.WGSL;
await Promise.all([
import("../ShadersWGSL/glowMapMerge.fragment"),
import("../ShadersWGSL/glowMapMerge.vertex"),
import("../ShadersWGSL/glowBlurPostProcess.fragment"),
]);
} else {
await Promise.all([import("../Shaders/glowMapMerge.fragment"), import("../Shaders/glowMapMerge.vertex"), import("../Shaders/glowBlurPostProcess.fragment")]);
}

await super._initShaderSourceAsync(forceGLSL);
}

/**
* Get the effect name of the layer.
* @returns The effect name
Expand All @@ -251,7 +267,18 @@ export class GlowLayer extends EffectLayer {
}

// Effect
return this._engine.createEffect("glowMapMerge", [VertexBuffer.PositionKind], ["offset"], ["textureSampler", "textureSampler2"], defines);
return this._engine.createEffect(
"glowMapMerge",
[VertexBuffer.PositionKind],
["offset"],
["textureSampler", "textureSampler2"],
defines,
undefined,
undefined,
undefined,
undefined,
this.shaderLanguage
);
}

/**
Expand Down
96 changes: 83 additions & 13 deletions packages/dev/core/src/Layers/highlightLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,9 @@ import { Logger } from "../Misc/logger";
import { RegisterClass } from "../Misc/typeStore";
import { Color4, Color3 } from "../Maths/math.color";

import "../Shaders/glowMapMerge.fragment";
import "../Shaders/glowMapMerge.vertex";
import "../Shaders/glowBlurPostProcess.fragment";
import "../Layers/effectLayerSceneComponent";
import { SerializationHelper } from "../Misc/decorators.serialization";
import { GetExponentOfTwo } from "../Misc/tools.functions";
import { ShaderLanguage } from "core/Materials/shaderLanguage";

declare module "../abstractScene" {
export interface AbstractScene {
Expand All @@ -54,6 +51,10 @@ AbstractScene.prototype.getHighlightLayerByName = function (name: string): Nulla
return null;
};

interface IBlurPostProcess extends PostProcess {
kernel: number;
}

/**
* Special Glow Blur post process only blurring the alpha channel
* It enforces keeping the most luminous color in the color channel.
Expand All @@ -69,14 +70,47 @@ class GlowBlurPostProcess extends PostProcess {
engine?: AbstractEngine,
reusable?: boolean
) {
super(name, "glowBlurPostProcess", ["screenSize", "direction", "blurWidth"], null, options, camera, samplingMode, engine, reusable);
super(
name,
"glowBlurPostProcess",
["screenSize", "direction", "blurWidth"],
null,
options,
camera,
samplingMode,
engine,
reusable,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
true
);

this.onApplyObservable.add((effect: Effect) => {
effect.setFloat2("screenSize", this.width, this.height);
effect.setVector2("direction", this.direction);
effect.setFloat("blurWidth", this.kernel);
});
}

protected override async _initShaderSourceAsync(forceGLSL = false) {
const engine = this.getEngine();

this._shadersLoaded = false;
if (engine.isWebGPU && !forceGLSL) {
this._shaderLanguage = ShaderLanguage.WGSL;

await import("../ShadersWGSL/glowBlurPostProcess.fragment");
} else {
await import("../Shaders/glowBlurPostProcess.fragment");
}

this._shadersLoaded = true;
}
}

/**
Expand Down Expand Up @@ -135,6 +169,11 @@ export interface IHighlightLayerOptions {
* The type of the main texture. Default: TEXTURETYPE_UNSIGNED_INT
*/
mainTextureType: number;

/**
* Use the GLSL code generation for the shader (even on WebGPU). Default is false
*/
forceGLSL: boolean;
}

/**
Expand Down Expand Up @@ -275,8 +314,8 @@ export class HighlightLayer extends EffectLayer {
@serialize("options")
private _options: IHighlightLayerOptions;
private _downSamplePostprocess: PassPostProcess;
private _horizontalBlurPostprocess: GlowBlurPostProcess;
private _verticalBlurPostprocess: GlowBlurPostProcess;
private _horizontalBlurPostprocess: IBlurPostProcess;
private _verticalBlurPostprocess: IBlurPostProcess;
private _blurTexture: RenderTargetTexture;

private _meshes: Nullable<{ [id: string]: Nullable<IHighlightLayerMesh> }> = {};
Expand All @@ -293,7 +332,8 @@ export class HighlightLayer extends EffectLayer {
scene?: Scene,
options?: Partial<IHighlightLayerOptions>
) {
super(name, scene);
super(name, scene, options !== undefined ? !!options.forceGLSL : false);

this.neutralColor = HighlightLayer.NeutralColor;

// Warn on stencil
Expand All @@ -311,6 +351,7 @@ export class HighlightLayer extends EffectLayer {
camera: null,
renderingGroupId: -1,
mainTextureType: Constants.TEXTURETYPE_UNSIGNED_INT,
forceGLSL: false,
...options,
};

Expand All @@ -328,6 +369,23 @@ export class HighlightLayer extends EffectLayer {
this._shouldRender = false;
}

protected override async _initShaderSourceAsync(forceGLSL = false) {
const engine = this._scene.getEngine();

if (engine.isWebGPU && !forceGLSL && !EffectLayer.ForceGLSL) {
this._shaderLanguage = ShaderLanguage.WGSL;
await Promise.all([
import("../ShadersWGSL/glowMapMerge.fragment"),
import("../ShadersWGSL/glowMapMerge.vertex"),
import("../ShadersWGSL/glowBlurPostProcess.fragment"),
]);
} else {
await Promise.all([import("../Shaders/glowMapMerge.fragment"), import("../Shaders/glowMapMerge.vertex"), import("../Shaders/glowBlurPostProcess.fragment")]);
}

await super._initShaderSourceAsync(forceGLSL);
}

/**
* Get the effect name of the layer.
* @returns The effect name
Expand All @@ -347,7 +405,18 @@ export class HighlightLayer extends EffectLayer {
*/
protected _createMergeEffect(): Effect {
// Effect
return this._engine.createEffect("glowMapMerge", [VertexBuffer.PositionKind], ["offset"], ["textureSampler"], this._options.isStroke ? "#define STROKE \n" : undefined);
return this._engine.createEffect(
"glowMapMerge",
[VertexBuffer.PositionKind],
["offset"],
["textureSampler"],
this._options.isStroke ? "#define STROKE \n" : undefined,
undefined,
undefined,
undefined,
undefined,
this._shaderLanguage
);
}

/**
Expand Down Expand Up @@ -441,10 +510,11 @@ export class HighlightLayer extends EffectLayer {
false,
textureType
);
this._horizontalBlurPostprocess.width = blurTextureWidth;
this._horizontalBlurPostprocess.height = blurTextureHeight;
this._horizontalBlurPostprocess.externalTextureSamplerBinding = true;
this._horizontalBlurPostprocess.onApplyObservable.add((effect) => {
const horizontalBlurPostprocess = this._horizontalBlurPostprocess as BlurPostProcess;
horizontalBlurPostprocess.width = blurTextureWidth;
horizontalBlurPostprocess.height = blurTextureHeight;
horizontalBlurPostprocess.externalTextureSamplerBinding = true;
horizontalBlurPostprocess.onApplyObservable.add((effect) => {
effect.setTexture("textureSampler", this._mainTexture);
});

Expand Down
14 changes: 14 additions & 0 deletions packages/dev/core/src/Layers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,17 @@ export * from "./glowLayer";
export * from "./highlightLayer";
export * from "./layer";
export * from "./layerSceneComponent";

// EffectLayer
export * from "../Shaders/glowMapGeneration.fragment";
export * from "../Shaders/glowMapGeneration.vertex";
export * from "../ShadersWGSL/glowMapGeneration.fragment";
export * from "../ShadersWGSL/glowMapGeneration.vertex";

// Highlights
export * from "../Shaders/glowMapMerge.fragment";
export * from "../Shaders/glowMapMerge.vertex";
export * from "../Shaders/glowBlurPostProcess.fragment";
export * from "../ShadersWGSL/glowMapMerge.fragment";
export * from "../ShadersWGSL/glowMapMerge.vertex";
export * from "../ShadersWGSL/glowBlurPostProcess.fragment";
Loading

0 comments on commit 8749f61

Please sign in to comment.