Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ShaderLab support MRT #2452

Merged
merged 32 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
ad984da
feat: add mrt syntax
Sway007 Dec 5, 2024
e40dfd2
Merge branch 'dev/1.4' of github.com:galacean/engine into feat/shader…
Sway007 Dec 5, 2024
ebb3b91
feat: mrt code gen
Sway007 Dec 5, 2024
6368479
feat: code opt
Sway007 Dec 5, 2024
9e654a0
feat: code opt
Sway007 Dec 9, 2024
79f87b2
feat: add e2e and unitest case
Sway007 Dec 9, 2024
07f5f60
fix: code gen of invalid mrt property
Sway007 Dec 9, 2024
85adcc1
Merge branch 'dev/1.4' of github.com:galacean/engine into feat/shader…
Sway007 Dec 12, 2024
dfbfb80
revert: vitest config
Sway007 Dec 12, 2024
f5ddea8
feat: code opt
Sway007 Dec 13, 2024
5a3c447
feat: code opt
Sway007 Dec 16, 2024
e141551
Merge branch 'dev/1.4' of github.com:galacean/engine into feat/shader…
Sway007 Dec 17, 2024
e59f8b2
feat: code opt
Sway007 Dec 17, 2024
4a7c9fc
fix: resolve confict of mrt return
Sway007 Dec 17, 2024
31a90ee
feat: code opt
Sway007 Dec 17, 2024
58892a5
fix: type error
Sway007 Dec 17, 2024
c4be16e
feat: code opt
Sway007 Dec 17, 2024
8604847
feat: update test case
Sway007 Dec 17, 2024
85b7291
feat: code opt
Sway007 Dec 18, 2024
aa905b1
feat: code opt
Sway007 Dec 18, 2024
ab68939
feat: code opt
Sway007 Dec 19, 2024
3ebe87e
feat: code opt
Sway007 Dec 23, 2024
78c1919
refactor: opt code
GuoLei1990 Dec 23, 2024
20b1f01
fix: unitest
Sway007 Dec 23, 2024
3e6f35e
feat: rename ENonTerminal -> NoneTerminal
Sway007 Dec 23, 2024
15728c0
refactor: opt code
GuoLei1990 Dec 23, 2024
207dd18
feat: code opt
Sway007 Dec 24, 2024
528c1e2
feat: code opt
Sway007 Dec 24, 2024
324cd87
refactor: code opt
Sway007 Dec 24, 2024
a7d3cdf
feat: code opt
Sway007 Dec 24, 2024
1350cc9
feat: code opt
Sway007 Dec 24, 2024
e0ec460
feat: rename
Sway007 Dec 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions e2e/case/shaderLab-mrt.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* @title ShaderLab MRT
* @category Material
*/

import { Camera, Color, Logger, Material, MeshRenderer, PrimitiveMesh, Shader, WebGLEngine } from "@galacean/engine";
import { ShaderLab } from "@galacean/engine-shader-lab";
import { initScreenshot, updateForE2E } from "./.mockForE2E";

Check warning on line 8 in e2e/case/shaderLab-mrt.ts

View check run for this annotation

Codecov / codecov/patch

e2e/case/shaderLab-mrt.ts#L6-L8

Added lines #L6 - L8 were not covered by tests

const shaderLab = new ShaderLab();

Check warning on line 10 in e2e/case/shaderLab-mrt.ts

View check run for this annotation

Codecov / codecov/patch

e2e/case/shaderLab-mrt.ts#L10

Added line #L10 was not covered by tests

const shaderSource = `Shader "/custom.gs" {

Check warning on line 12 in e2e/case/shaderLab-mrt.ts

View check run for this annotation

Codecov / codecov/patch

e2e/case/shaderLab-mrt.ts#L12

Added line #L12 was not covered by tests
SubShader "Default" {
UsePass "pbr/Default/ShadowCaster"

Pass "Pass0" {
struct Attributes {
vec3 POSITION;
vec2 TEXCOORD_0;
vec4 JOINTS_0;
vec4 WEIGHTS_0;
};

struct Varyings {
vec2 uv;
};

mat4 renderer_MVPMat;

vec4 material_BaseColor;


VertexShader = vert;
FragmentShader = frag;

Varyings vert(Attributes attr) {
Varyings v;

vec4 position = vec4(attr.POSITION, 1.0);

// Skin
#ifdef RENDERER_HAS_SKIN
mat4 skinMatrix = getSkinMatrix(attr);
position = skinMatrix * position;
#endif

gl_Position = renderer_MVPMat * position;
v.uv = attr.TEXCOORD_0;

return v;
}

struct mrt {
layout(location = 0) vec4 fragColor0;
layout(location = 1) vec4 fragColor1;
};

mrt frag(Varyings v) {
mrt o;

vec4 baseColor = material_BaseColor;

#ifdef MATERIAL_HAS_BASETEXTURE
vec4 textureColor = texture2D(material_BaseTexture, v.uv);
#ifndef ENGINE_IS_COLORSPACE_GAMMA
textureColor = gammaToLinear(textureColor);
#endif
baseColor *= textureColor;
#endif

#ifdef MATERIAL_IS_ALPHA_CUTOFF
if( baseColor.a < material_AlphaCutoff ) {
discard;
}
#endif

o.fragColor0 = baseColor;
o.fragColor1 = baseColor;
zhuxudong marked this conversation as resolved.
Show resolved Hide resolved

return o;
}
}
}
}`;

Logger.enable();
WebGLEngine.create({ canvas: "canvas", shaderLab }).then((engine) => {
engine.canvas.resizeByClientSize();

Check warning on line 88 in e2e/case/shaderLab-mrt.ts

View check run for this annotation

Codecov / codecov/patch

e2e/case/shaderLab-mrt.ts#L86-L88

Added lines #L86 - L88 were not covered by tests

const shader = Shader.create(shaderSource);
const scene = engine.sceneManager.activeScene;
const rootEntity = scene.createRootEntity();

Check warning on line 92 in e2e/case/shaderLab-mrt.ts

View check run for this annotation

Codecov / codecov/patch

e2e/case/shaderLab-mrt.ts#L90-L92

Added lines #L90 - L92 were not covered by tests

// camera
const cameraEntity = rootEntity.createChild("cameraNode");
cameraEntity.transform.setPosition(0, 0, 5);
const camera = cameraEntity.addComponent(Camera);

Check warning on line 97 in e2e/case/shaderLab-mrt.ts

View check run for this annotation

Codecov / codecov/patch

e2e/case/shaderLab-mrt.ts#L95-L97

Added lines #L95 - L97 were not covered by tests

// sphere
{
const sphere = rootEntity.createChild("sphere");
sphere.transform.position.x = -1;
const renderer = sphere.addComponent(MeshRenderer);
renderer.mesh = PrimitiveMesh.createSphere(engine);
const material = new Material(engine, shader);
material.shaderData.setColor("material_BaseColor", new Color(1, 0, 0, 0.2));
renderer.setMaterial(material);
}

Check warning on line 108 in e2e/case/shaderLab-mrt.ts

View check run for this annotation

Codecov / codecov/patch

e2e/case/shaderLab-mrt.ts#L100-L108

Added lines #L100 - L108 were not covered by tests

updateForE2E(engine);

Check warning on line 110 in e2e/case/shaderLab-mrt.ts

View check run for this annotation

Codecov / codecov/patch

e2e/case/shaderLab-mrt.ts#L110

Added line #L110 was not covered by tests

initScreenshot(engine, camera);
});

Check warning on line 113 in e2e/case/shaderLab-mrt.ts

View check run for this annotation

Codecov / codecov/patch

e2e/case/shaderLab-mrt.ts#L112-L113

Added lines #L112 - L113 were not covered by tests
5 changes: 5 additions & 0 deletions e2e/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@
caseFileName: "material-shaderLab",
threshold: 0.2
},
shaderLabMRT: {
category: "Material",
caseFileName: "shaderLab-mrt",
threshold: 0.2
},

Check warning on line 117 in e2e/config.ts

View check run for this annotation

Codecov / codecov/patch

e2e/config.ts#L113-L117

Added lines #L113 - L117 were not covered by tests
shaderReplacement: {
category: "Material",
caseFileName: "material-shaderReplacement",
Expand Down
3 changes: 3 additions & 0 deletions e2e/fixtures/originImage/Material_shaderLab-mrt.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion packages/shader-lab/src/GSError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ export class GSError extends Error {
if (i === start.line) {
remarkStart = start.column;
paddingLength += start.column;
} else if (i === end.line) {
}
if (i === end.line) {
GuoLei1990 marked this conversation as resolved.
Show resolved Hide resolved
remarkEnd = end.column;
}
const remarkLength = Math.max(remarkEnd - remarkStart, 1);
Expand Down
8 changes: 4 additions & 4 deletions packages/shader-lab/src/ParserUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ENonTerminal, GrammarSymbol } from "./parser/GrammarSymbol";
import { NoneTerminal, GrammarSymbol } from "./parser/GrammarSymbol";
import { BaseToken as Token } from "./common/BaseToken";
import { EKeyword, ETokenType, GalaceanDataType } from "./common";
import { TreeNode } from "./parser/AST";
Expand All @@ -7,7 +7,7 @@
// #endif

export class ParserUtils {
static unwrapNodeByType<T = TreeNode>(node: TreeNode, type: ENonTerminal): T | undefined {
static unwrapNodeByType<T = TreeNode>(node: TreeNode, type: NoneTerminal): T | undefined {
const child = node.children[0];
if (child instanceof Token) return;
if (child.nt === type) return child as T;
Expand All @@ -30,12 +30,12 @@
if (this.isTerminal(sm)) {
return ETokenType[sm] ?? EKeyword[sm];
}
return ENonTerminal[sm];
return NoneTerminal[sm];
}
// #endif

static isTerminal(sm: GrammarSymbol) {
return sm < ENonTerminal.START;
return sm < NoneTerminal.START;

Check warning on line 38 in packages/shader-lab/src/ParserUtils.ts

View check run for this annotation

Codecov / codecov/patch

packages/shader-lab/src/ParserUtils.ts#L38

Added line #L38 was not covered by tests
}

/**
Expand Down
6 changes: 3 additions & 3 deletions packages/shader-lab/src/ShaderLabUtils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ClearableObjectPool, IPoolElement } from "@galacean/engine";
import { ClearableObjectPool, IPoolElement, Logger } from "@galacean/engine";
import { GSErrorName } from "./GSError";
import { ShaderRange } from "./common/ShaderRange";
import { ShaderPosition } from "./common/ShaderPosition";
Expand Down Expand Up @@ -27,11 +27,11 @@ export class ShaderLabUtils {
source: string,
location: ShaderRange | ShaderPosition,
file?: string
): Error {
): Error | undefined {
// #if _VERBOSE
return new GSError(errorName, message, location, source, file);
// #else
return new Error(`[${errorName}]: ${message}`);
Logger.error(message);
// #endif
Sway007 marked this conversation as resolved.
Show resolved Hide resolved
}
}
78 changes: 56 additions & 22 deletions packages/shader-lab/src/codeGen/CodeGenVisitor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ENonTerminal } from "../parser/GrammarSymbol";
import { NoneTerminal } from "../parser/GrammarSymbol";
import { BaseToken as Token } from "../common/BaseToken";
import { EKeyword, ShaderPosition, ShaderRange } from "../common";
import { ASTNode, TreeNode } from "../parser/AST";
Expand All @@ -11,10 +11,12 @@
// #if _VERBOSE
import { GSError } from "../GSError";
// #endif
import { ShaderLabUtils } from "../ShaderLabUtils";
import { Logger, ReturnableObjectPool } from "@galacean/engine";
import { TempArray } from "../TempArray";

export const V3_GL_FragColor = "GS_glFragColor";
export const V3_GL_FragData = "GS_glFragData";

/**
* @internal
* The code generator
Expand All @@ -23,6 +25,10 @@
// #if _VERBOSE
readonly errors: Error[] = [];
// #endif

abstract getFragDataCodeGen(index: string | number): string;
abstract getReferencedMRTPropText(index: string | number, ident: string): string;

protected static _tmpArrayPool = new ReturnableObjectPool(TempArray<string>, 10);

defaultCodeGen(children: NodeChild[]) {
Expand All @@ -41,12 +47,13 @@
}

visitPostfixExpression(node: ASTNode.PostfixExpression) {
if (node.children.length === 3) {
const context = VisitorContext.context;

const postExpr = node.children[0] as ASTNode.PostfixExpression;
const children = node.children;
const derivationLength = children.length;
const context = VisitorContext.context;

const prop = node.children[2];
if (derivationLength === 3) {
const postExpr = children[0] as ASTNode.PostfixExpression;
const prop = children[2];

if (prop instanceof Token) {
if (context.isAttributeStruct(<string>postExpr.type)) {
Expand All @@ -65,12 +72,36 @@
}
// #endif
return prop.lexeme;
} else if (context.isMRTStruct(<string>postExpr.type)) {
const error = context.referenceMRTProp(prop);
// #if _VERBOSE
if (error) {
this.errors.push(<GSError>error);
}

Check warning on line 80 in packages/shader-lab/src/codeGen/CodeGenVisitor.ts

View check run for this annotation

Codecov / codecov/patch

packages/shader-lab/src/codeGen/CodeGenVisitor.ts#L79-L80

Added lines #L79 - L80 were not covered by tests
// #endif
return prop.lexeme;
}

return `${postExpr.codeGen(this)}.${prop.lexeme}`;
} else {
return `${postExpr.codeGen(this)}.${prop.codeGen(this)}`;
}
} else if (derivationLength === 4) {
const identNode = children[0] as ASTNode.PostfixExpression;
const indexNode = children[2] as ASTNode.Expression;
const identLexeme = identNode.codeGen(this);
const indexLexeme = indexNode.codeGen(this);
if (identLexeme === "gl_FragData") {
// #if _VERBOSE
if (context._referencedVaryingList[V3_GL_FragColor]) {
this._reportError(identNode.location, "cannot use both gl_FragData and gl_FragColor");
}
// #endif
const mrtLexeme = this.getFragDataCodeGen(indexLexeme);
context._referencedMRTList[mrtLexeme] = this.getReferencedMRTPropText(indexLexeme, mrtLexeme);
return mrtLexeme;
}
return `${identLexeme}[${indexLexeme}]`;
}
return this.defaultCodeGen(node.children);
}
Expand Down Expand Up @@ -110,7 +141,7 @@

visitStatementList(node: ASTNode.StatementList): string {
const children = node.children as TreeNode[];
if (node.children.length === 1) {
if (children.length === 1) {
return children[0].codeGen(this);
} else {
return `${children[0].codeGen(this)}\n${children[1].codeGen(this)}`;
Expand All @@ -126,22 +157,24 @@
}

visitGlobalVariableDeclaration(node: ASTNode.VariableDeclaration): string {
const fullType = node.children[0];
const children = node.children;
const fullType = children[0];
if (fullType instanceof ASTNode.FullySpecifiedType && fullType.typeSpecifier.isCustom) {
VisitorContext.context.referenceGlobal(<string>fullType.type, ESymbolType.STRUCT);
}
return this.defaultCodeGen(node.children);
return this.defaultCodeGen(children);
}

visitDeclaration(node: ASTNode.Declaration): string {
const child = node.children[0];
if (
child instanceof ASTNode.InitDeclaratorList &&
child.typeInfo.typeLexeme === VisitorContext.context.varyingStruct?.ident?.lexeme
) {
return "";
const { context } = VisitorContext;
const children = node.children;
const child = children[0];

if (child instanceof ASTNode.InitDeclaratorList) {
const typeLexeme = child.typeInfo.typeLexeme;
if (context.isVaryingStruct(typeLexeme) || context.isMRTStruct(typeLexeme)) return "";
Sway007 marked this conversation as resolved.
Show resolved Hide resolved
}
return this.defaultCodeGen(node.children);
return this.defaultCodeGen(children);
}

visitFunctionProtoType(node: ASTNode.FunctionProtoType): string {
Expand Down Expand Up @@ -174,24 +207,25 @@
}

visitJumpStatement(node: ASTNode.JumpStatement): string {
const cmd = node.children[0] as Token;
const children = node.children;
const cmd = children[0] as Token;
if (cmd.type === EKeyword.RETURN) {
const expr = node.children[1];
const expr = children[1];
if (expr instanceof ASTNode.Expression) {
const returnVar = ParserUtils.unwrapNodeByType<ASTNode.VariableIdentifier>(
expr,
ENonTerminal.variable_identifier
NoneTerminal.variable_identifier
);
if (returnVar?.typeInfo === VisitorContext.context.varyingStruct?.ident?.lexeme) {
return "";
}
const returnFnCall = ParserUtils.unwrapNodeByType<ASTNode.FunctionCall>(expr, ENonTerminal.function_call);
const returnFnCall = ParserUtils.unwrapNodeByType<ASTNode.FunctionCall>(expr, NoneTerminal.function_call);
if (returnFnCall?.type === VisitorContext.context.varyingStruct?.ident?.lexeme) {
return `${expr.codeGen(this)};`;
}
}
}
return this.defaultCodeGen(node.children);
return this.defaultCodeGen(children);
}

visitFunctionIdentifier(node: ASTNode.FunctionIdentifier): string {
Expand Down
Loading
Loading