Skip to content
Draft
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
5d37ccc
fix(shader-lab): resolve GVec4 generic return type for texture() buil…
zhuxudong Mar 26, 2026
727bcec
fix(shader-lab): simplify resolveGenericReturnType, fix textureCube/t…
zhuxudong Mar 26, 2026
b69233b
implement HorizontalBillboard render mode (#2938)
hhhhkrx Mar 23, 2026
8a0a605
fix(shader-lab): return TypeAny for unresolved generic builtin return…
zhuxudong Mar 26, 2026
889b096
fix(shader-lab): resolve GVec4 generic return type for texture() buil…
zhuxudong Mar 26, 2026
2d25129
fix(shader-lab): simplify resolveGenericReturnType, fix textureCube/t…
zhuxudong Mar 26, 2026
4b3d632
fix(shader-lab): return TypeAny for unresolved generic builtin return…
zhuxudong Mar 26, 2026
2f6712a
fix(shader-lab): transform struct member access in #define values dur…
zhuxudong Mar 26, 2026
a0e6fc8
test(shader-lab): add transformation result assertions for define-str…
zhuxudong Mar 26, 2026
60480bb
test(shader-lab): enrich define-struct-access tests with usage assert…
zhuxudong Mar 26, 2026
d05187b
fix(shader-lab): support global #define with cross-stage struct var t…
zhuxudong Mar 26, 2026
828752d
fix(shader-lab): skip type inference for member access macros in sema…
zhuxudong Mar 26, 2026
7b18aef
test(shader-lab): add Cocos FSInput pattern test for member access ma…
zhuxudong Mar 26, 2026
80e6e3e
fix(shader-lab): handle global struct-typed variables and simplify ma…
zhuxudong Mar 26, 2026
bfe4e9e
fix(shader-lab): add missing semicolon in GLES100 fragment return con…
zhuxudong Mar 26, 2026
b902c21
feat: merge code
cptbtptpbcptdtptp Mar 27, 2026
b714609
fix: temp
cptbtptpbcptdtptp Mar 27, 2026
1749077
fix: temp
cptbtptpbcptdtptp Mar 27, 2026
c662731
fix: entity disable
cptbtptpbcptdtptp Mar 27, 2026
d0dada8
fix: keep mesh data
cptbtptpbcptdtptp Mar 27, 2026
3cf1883
fix(shader-lab): allow logical NOT operator on numeric operands in pr…
zhuxudong Mar 27, 2026
41f024e
fix(camera): make invViewProjMat ignore scale consistently with viewM…
cptbtptpbcptdtptp Mar 27, 2026
63dff37
test(camera): add unit test for invViewProjMat scale consistency
cptbtptpbcptdtptp Mar 27, 2026
ca0d518
fix: use project type
cptbtptpbcptdtptp Mar 28, 2026
13521cc
fix(loader): componentRef 解析支持 clone entity 子树查找
luzhuang Mar 29, 2026
a6156ba
fix(clone): prefab 克隆时自动 deep clone 同类型 Object 属性,防止共享状态
luzhuang Mar 29, 2026
d9a2674
fix(animation): normalize single-root clip binding paths
luzhuang Mar 29, 2026
63c715b
fix(animation): add per-instance speed to AnimatorStatePlayData
luzhuang Mar 30, 2026
13052d2
feat(ui): add SpriteSizeMode to Image component
luzhuang Mar 30, 2026
15b19af
feat: particle bug not fix
luzhuang Apr 1, 2026
207a381
feat(2d): add FilledSpriteAssembler and sprite filled mode support
cptbtptpbcptdtptp Apr 2, 2026
ca5252a
feat: supported filled
cptbtptpbcptdtptp Apr 7, 2026
5c2edee
fix: clone and sibling index
cptbtptpbcptdtptp Apr 15, 2026
40006d9
fix: raycast collider layer
cptbtptpbcptdtptp Apr 15, 2026
4fe4fae
fix(shader): add missing camera_VPMat declaration in Transform.glsl
zhuxudong Apr 15, 2026
3071f90
fix(loader): always create GLTF_ROOT container for consistent animati…
luzhuang Mar 26, 2026
40734f8
fix(e2e): update blendShape e2e tests for GLTF_ROOT nesting
luzhuang Mar 26, 2026
5abb3f5
fix(loader): restore single-root GLTF scene without GLTF_ROOT wrapper
luzhuang Apr 15, 2026
b3bf58a
Revert "fix(loader): restore single-root GLTF scene without GLTF_ROOT…
luzhuang Apr 15, 2026
069f182
fix(loader): normalize gltf wrapper and skin roots
luzhuang Apr 15, 2026
f5be424
fix: raycast and clone
cptbtptpbcptdtptp Apr 15, 2026
5d7e6af
Merge remote-tracking branch 'origin/fix/shaderlab' into fix/shaderlab
cptbtptpbcptdtptp Apr 15, 2026
bc35a61
fix: revert code
cptbtptpbcptdtptp Apr 15, 2026
d5b6dd9
fix(loader): add audio extension to AudioLoader supported types
luzhuang Apr 15, 2026
bdf5490
fix(physics-physx): raycast 和 sweep 跳过 initial overlap
luzhuang Apr 15, 2026
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
8 changes: 8 additions & 0 deletions packages/shader-lab/src/Preprocessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ShaderLib } from "@galacean/engine";
export enum MacroValueType {
Number, // 1, 1.1
Symbol, // variable name
MemberAccess, // member access, e.g. input.v_uv, v.rgb
FunctionCall, // function call, e.g. clamp(a, 0.0, 1.0)
Other // shaderLab does not check this
}
Expand All @@ -27,6 +28,7 @@ export class Preprocessor {
private static readonly _macroRegex =
/^\s*#define\s+(\w+)[ ]*(\(([^)]*)\))?[ ]+(\(?\w+\)?.*?)(?:\/\/.*|\/\*.*?\*\/)?\s*$/gm;
private static readonly _symbolReg = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
private static readonly _memberAccessReg = /^([a-zA-Z_][a-zA-Z0-9_]*)(\.[a-zA-Z_][a-zA-Z0-9_]*)+$/;
private static readonly _funcCallReg = /^([a-zA-Z_][a-zA-Z0-9_]*)\s*\((.*)\)$/;
private static readonly _macroDefineIncludeMap = new Map<string, MacroDefineList>();

Expand Down Expand Up @@ -61,6 +63,10 @@ export class Preprocessor {
const referencedName = valueType === MacroValueType.FunctionCall ? info.functionCallName : info.value;
if (info.params.indexOf(referencedName) !== -1) continue;
if (out.indexOf(referencedName) === -1) out.push(referencedName);
} else if (valueType === MacroValueType.MemberAccess) {
// Extract root symbol: "input.v_uv" → "input"
const rootName = info.value.substring(0, info.value.indexOf("."));
if (out.indexOf(rootName) === -1) out.push(rootName);
} else if (valueType === MacroValueType.Other) {
// #if _VERBOSE
Logger.warn(
Expand Down Expand Up @@ -110,6 +116,8 @@ export class Preprocessor {
valueType = MacroValueType.Number;
} else if (this._symbolReg.test(value)) {
valueType = MacroValueType.Symbol;
} else if (this._memberAccessReg.test(value)) {
valueType = MacroValueType.MemberAccess;
} else {
const callMatch = this._funcCallReg.exec(value);
if (callMatch) {
Expand Down
118 changes: 116 additions & 2 deletions packages/shader-lab/src/codeGen/CodeGenVisitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,19 @@ export abstract class CodeGenVisitor {

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

protected static readonly _memberAccessReg = /\b(\w+)\.(\w+)\b/g;

defaultCodeGen(children: NodeChild[]) {
const pool = CodeGenVisitor._tmpArrayPool;
let ret = pool.get();
ret.dispose();
for (const child of children) {
if (child instanceof BaseToken) {
ret.array.push(child.lexeme);
if (child.type === Keyword.MACRO_DEFINE_EXPRESSION) {
ret.array.push(this._transformMacroDefineValue(child.lexeme));
} else {
ret.array.push(child.lexeme);
}
} else {
ret.array.push(child.codeGen(this));
}
Expand All @@ -46,6 +52,32 @@ export abstract class CodeGenVisitor {
return ret.array.join(" ");
}

protected _transformMacroDefineValue(
lexeme: string,
overrideMap?: Record<string, "varying" | "attribute" | "mrt">
): string {
const context = VisitorContext.context;
const structVarMap = overrideMap ?? context._structVarMap;
if (!structVarMap) return lexeme;

const spaceIdx = lexeme.indexOf(" ");
if (spaceIdx === -1) return lexeme;

const macroName = lexeme.substring(0, spaceIdx);
let value = lexeme.substring(spaceIdx);

const reg = CodeGenVisitor._memberAccessReg;
reg.lastIndex = 0;
value = value.replace(reg, (match, varName, propName) => {
const role = structVarMap[varName];
if (!role) return match;
context.referenceStructPropByName(role, propName);
return propName;
});

return macroName + value;
}

visitPostfixExpression(node: ASTNode.PostfixExpression): string {
const children = node.children;
const derivationLength = children.length;
Expand Down Expand Up @@ -212,7 +244,19 @@ export abstract class CodeGenVisitor {
const children = node.children;
const fullType = children[0];
if (fullType instanceof ASTNode.FullySpecifiedType && fullType.typeSpecifier.isCustom) {
VisitorContext.context.referenceGlobal(<string>fullType.type, ESymbolType.STRUCT);
const context = VisitorContext.context;
const typeLexeme = fullType.typeSpecifier.lexeme;
const role = context.getStructRole(typeLexeme);
if (role) {
// Global variable of a varying/attribute/mrt struct type (e.g. "Varyings o;").
// Don't output as uniform; register the variable in struct var maps instead.
const ident = children[1];
if (ident instanceof BaseToken) {
context.registerStructVar(ident.lexeme, role);
}
return "";
}
context.referenceGlobal(<string>fullType.type, ESymbolType.STRUCT);
}
return `uniform ${this.defaultCodeGen(children)}`;
}
Expand Down Expand Up @@ -351,6 +395,8 @@ export abstract class CodeGenVisitor {
const fnName = fnNode.protoType.ident.lexeme;
const context = VisitorContext.context;

this._collectStructVars(fnNode, context);

if (fnName == context.stageEntry) {
const statements = fnNode.statements.codeGen(this);
return `void main() ${statements}`;
Expand All @@ -359,6 +405,74 @@ export abstract class CodeGenVisitor {
}
}

private _collectStructVars(fnNode: ASTNode.FunctionDefinition, context: VisitorContext): void {
const map = context._structVarMap;
// Clear previous function's mappings
for (const key in map) delete map[key];

// Collect from function parameters
const paramList = fnNode.protoType.parameterList;
if (paramList) {
for (const param of paramList) {
if (param.ident && param.typeInfo && typeof param.typeInfo.type === "string") {
const role = context.getStructRole(param.typeInfo.typeLexeme);
if (role) map[param.ident.lexeme] = role;
}
}
}

// Collect from local variable declarations in function body
this._collectStructVarsFromNode(fnNode.statements, context, map);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

private _collectStructVarsFromNode(
node: TreeNode,
context: VisitorContext,
map: Record<string, "varying" | "attribute" | "mrt">
): void {
const children = node.children;
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child instanceof ASTNode.InitDeclaratorList) {
const typeLexeme = child.typeInfo?.typeLexeme;
if (typeLexeme) {
const role = context.getStructRole(typeLexeme);
if (role) {
// Extract variable name from SingleDeclaration or comma-separated identifiers
this._extractVarNamesFromInitDeclaratorList(child, map, role);
}
}
} else if (child instanceof TreeNode) {
this._collectStructVarsFromNode(child, context, map);
}
}
}

protected _extractVarNamesFromInitDeclaratorList(
node: ASTNode.InitDeclaratorList,
map: Record<string, "varying" | "attribute" | "mrt">,
role: "varying" | "attribute" | "mrt"
): void {
const children = node.children;
if (children.length === 1) {
// SingleDeclaration: type ident
const singleDecl = children[0] as ASTNode.SingleDeclaration;
const identChildren = singleDecl.children;
if (identChildren.length >= 2 && identChildren[1] instanceof BaseToken) {
map[identChildren[1].lexeme] = role;
}
} else if (children.length >= 3) {
// InitDeclaratorList , ident ...
const initDeclList = children[0];
if (initDeclList instanceof ASTNode.InitDeclaratorList) {
this._extractVarNamesFromInitDeclaratorList(initDeclList, map, role);
}
if (children[2] instanceof BaseToken) {
map[children[2].lexeme] = role;
}
}
}

protected _reportError(loc: ShaderRange | ShaderPosition, message: string): void {
// #if _VERBOSE
this.errors.push(new GSError(GSErrorName.CompilationError, message, loc, ShaderLab._processingPassText));
Expand Down
Loading
Loading