WGSL PostProcess Vertex shader building wrong

I am trying to use a post process effect with a custom fragment shader. When I run my app and look at the console, I am hit with a ton of invalid shader warnings for both the fragment and vertex. It should be using the built-in ShadersWGSL/postprocess.vertex.fx shader.

The first warning I always get is: Invalid fragment shader: The varying named “vUV” is not declared in the vertex shader! This declaration will be ignored.

Which is implying that the vertex shader is not exporting the vUV varying. I did a debug shader code on the engine to see and the vertex shader was in fact missing the varying in the fragmentInputs struct. The result of that dump is here:

//#define SHADER_NAME vertex:postprocess
diagnostic(off, chromium.unreachable_code);
struct VertexInputs {
@builtin(vertex_index) vertexIndex : u32,
@builtin(instance_index) instanceIndex : u32,
@location(0) position : vec3f,
};
var vertexInputs : VertexInputs;
struct FragmentInputs {
@builtin(position) position : vec4,

};
var vertexOutputs : FragmentInputs;

struct LeftOver {
world : mat4x4,
viewProjection : mat4x4f,
color : vec4f,
aspectRatio : f32,
h_color: vec3,
};
@group(1) @binding(1) var uniforms : LeftOver;
const IS_NDC_HALF_ZRANGE = true;
struct Internals {
yFactor_: f32,
textureOutputHeight_: f32,
};
@group(1) @binding(0) var internals : Internals;

const madd=vec2(0.5,0.5);
//#define CUSTOM_VERTEX_DEFINITIONS
@vertex
fn main(input : VertexInputs)->FragmentInputs {
vertexInputs = input;

//#define CUSTOM_VERTEX_MAIN_BEGIN
vertexOutputs.vUV=(vertexInputs.position*madd+madd)*uniforms.scale;
vertexOutputs.position=vec4(vertexInputs.position,0.0,1.0);
//#define CUSTOM_VERTEX_MAIN_END
vertexOutputs.position.y = vertexOutputs.position.y * internals.yFactor_;
return vertexOutputs;
}

I create my post process like this

this._highlightEffect = new PostProcess('Highlight Effect', shadersPath + '/highlightEffect', {
    shaderLanguage: ShaderLanguage.WGSL,
    samplers: ['renderTargetTexture'],
    uniforms: ['aspectRatio', 'h_color'],
    camera: camera
});

And my shader has the correct variables at the top for a post process shader

uniform aspectRatio : f32;
uniform h_color: vec3<f32>;

// Varying
varying vUV : vec2<f32>;

var textureSamplerSampler : sampler;
var textureSampler: texture_2d<f32>;

var renderTargetTextureSampler : sampler;
var renderTargetTexture: texture_2d<f32>;

I tested this in the playground with a custom fragment shader and it worked.

I am using Babylon 8.36.1 on Windows, building into an angular app with webpack.

Any help is appreciated.

Thanks

Your WGSL code dump is very strange, because it doesn’t match the dump I get from this simple PG:

//#define SHADER_NAME vertex:postprocess
diagnostic(off, chromium.unreachable_code);
struct VertexInputs {
  @builtin(vertex_index) vertexIndex : u32,
  @builtin(instance_index) instanceIndex : u32,
@location(0) position : vec2<f32>,
};
var<private> vertexInputs : VertexInputs;
struct FragmentInputs {
  @builtin(position) position : vec4<f32>,
  @location(0) @interpolate(perspective, center) vUV : vec2<f32>,
};
var<private> vertexOutputs : FragmentInputs;

struct LeftOver {
  scale : vec2<f32>,
  degree : f32,
};
@group(1) @binding(3) var<uniform> uniforms : LeftOver;
const IS_NDC_HALF_ZRANGE = true;
struct Internals {
yFactor_: f32,
textureOutputHeight_: f32,
};
@group(1) @binding(2) var<uniform> internals : Internals;



const madd=vec2(0.5,0.5);
//#define CUSTOM_VERTEX_DEFINITIONS
@vertex
fn main(input : VertexInputs)->FragmentInputs {
  vertexInputs = input;

//#define CUSTOM_VERTEX_MAIN_BEGIN
vertexOutputs.vUV=(vertexInputs.position*madd+madd)*uniforms.scale;
vertexOutputs.position=vec4(vertexInputs.position,0.0,1.0);
//#define CUSTOM_VERTEX_MAIN_END
  vertexOutputs.position.y = vertexOutputs.position.y * internals.yFactor_;
  return vertexOutputs;
}

The whitespace is not the same. In addition, the declaration of certain variables should include the namespace “private” or “uniform,” which is not the case in your dump.

How did you generate this result? Was it by using engine.dbgShowShaderCode = true? The fact that it works in Playground indicates a problem in your application’s packaging, not in your code.

That is how I generated it in my application as well.

Im not packaging any differently from when I was using WebGL and the GLSL built in PP shader but Ill start focusing on the build output.

After closer inspection of your dump, all the <XXX> constructs are missing!

So, did you really copy this code from the console, or is it a copy from a html page (where <XXX> would be considered as tags and not copied)?

Copied from the DevTools console in Microsoft Edge

Apologies, the missing tags looks like a result of a bad paste into the forum text. They disappear if I paste the console log without enabling a code block first. This is the actual output.

diagnostic(off, chromium.unreachable_code);
struct VertexInputs {
  @builtin(vertex_index) vertexIndex : u32,
  @builtin(instance_index) instanceIndex : u32,
@location(0) position : vec3f,
};
var<private> vertexInputs : VertexInputs;
struct FragmentInputs {
  @builtin(position) position : vec4<f32>,

};
var<private> vertexOutputs : FragmentInputs;

struct LeftOver {
  world : mat4x4<f32>,
  viewProjection : mat4x4f,
  color : vec4f,
  aspectRatio : f32,
  highlightColor : vec3<f32>,
};
@group(1) @binding(1) var<uniform> uniforms : LeftOver;
const IS_NDC_HALF_ZRANGE = true;
struct Internals {
yFactor_: f32,
textureOutputHeight_: f32,
};
@group(1) @binding(0) var<uniform> internals : Internals;



const madd=vec2(0.5,0.5);
//#define CUSTOM_VERTEX_DEFINITIONS
@vertex
fn main(input : VertexInputs)->FragmentInputs {
  vertexInputs = input;

//#define CUSTOM_VERTEX_MAIN_BEGIN
vertexOutputs.vUV=(vertexInputs.position*madd+madd)*uniforms.scale;
vertexOutputs.position=vec4(vertexInputs.position,0.0,1.0);
//#define CUSTOM_VERTEX_MAIN_END
  vertexOutputs.position.y = vertexOutputs.position.y * internals.yFactor_;
  return vertexOutputs;
}
1 Like

I tested locally by removing the vUV declaration from the postprocess.vertex.fx, and I get an error:

WebGPU uncaptured error (1): [object GPUValidationError] - Error while parsing WGSL: :35:1 error: struct member vUV not found
vertexOutputs.vUV=(vertexInputs.position*madd+madd)*uniforms.scale;

It’s expected, as the vUV variable is not declared anymore.

In your dump, you do have the vertexOutputs.vUV=... line, without the declaration: I assume you also get this error in the console at some point?

With this setup, I do have the same output than you for FragmentInputs :

struct FragmentInputs {
  @builtin(position) position : vec4<f32>,

};

So, it seems the declaration is not in the postprocess.vertex.fx file that you use. Can you check it?

I do also get that error.

The shader in my node_modules/@babylonjs/core/ShadersWGSL/postprocess.vertex.js is

// Do not edit.

import { ShaderStore } from "../Engines/shaderStore.js";
const name = "postprocessVertexShader";
const shader = `attribute position: vec2<f32>;uniform scale: vec2<f32>;varying vUV: vec2<f32>;const madd=vec2(0.5,0.5);
#define CUSTOM_VERTEX_DEFINITIONS
@vertex
fn main(input : VertexInputs)->FragmentInputs {
#define CUSTOM_VERTEX_MAIN_BEGIN
vertexOutputs.vUV=(vertexInputs.position*madd+madd)*uniforms.scale;vertexOutputs.position=vec4(vertexInputs.position,0.0,1.0);
#define CUSTOM_VERTEX_MAIN_END
}
`;
// Sideeffect
if (!ShaderStore.ShadersStoreWGSL[name]) {
    ShaderStore.ShadersStoreWGSL[name] = shader;
}
/** @internal */
export const postprocessVertexShaderWGSL = { name, shader };
//# sourceMappingURL=postprocess.vertex.js.map

So, varying vUV: vec2<f32> is there…

The next step would be to put a debug breakpoint in Engines/WebGPU/webgpuShaderProcessorWGSL.ts, line 118 (inside the varyingProcessor method), and see if you hit this method when varying equals varying vUV: vec2<f32>. If yes, you should step in and see if you reach the line this._varyingsWGSL.push(" @location(${location}) ${interpolation} ${name} : ${varyingType},");.

Alternatively, if you can setup a live link that repro the problem, I could have a look.

I did the breakpoint and I do hit that function once with varying == varying vUV: vec2<f32> and it does reach this._varyingsWGSL.push

I will try to set up a lightweight repo I can share that recreates the issue

1 Like

I was able to recreate the problem in this repo that replicates my project setup

In this case my external post process fragment is just a copy of the black white wgsl fragment.

There are some differences in the shader dump (a lot more leftOver elements) but the core problem is still there.

Its in the readme but running it just requires doing npm i && npm run build:dev in the babylon-package directory, followed by npm i && npm run start in the test-app directory

Thanks for the repro!

I was able to find and fix the bug:

1 Like

Great to hear. Thank you for looking into this!