fix:bug
This commit is contained in:
119
server.js
119
server.js
@@ -81,6 +81,35 @@ function extractFunctionBlocks(code) {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
function buildFunctionMap(blocks) {
|
||||
const map = new Map();
|
||||
for (const block of blocks) {
|
||||
const m = block.match(/^\s*(?:void|float|vec[234]|mat[234]|int|uint)\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/m);
|
||||
if (!m) continue;
|
||||
map.set(m[1], block);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
function collectFunctionDependencies(fnMap, roots) {
|
||||
const keep = new Set();
|
||||
const queue = [...roots];
|
||||
while (queue.length) {
|
||||
const name = queue.shift();
|
||||
if (keep.has(name)) continue;
|
||||
const block = fnMap.get(name);
|
||||
if (!block) continue;
|
||||
keep.add(name);
|
||||
const callPattern = /\b([A-Za-z_][A-Za-z0-9_]*)\s*\(/g;
|
||||
let m;
|
||||
while ((m = callPattern.exec(block)) !== null) {
|
||||
const callee = m[1];
|
||||
if (fnMap.has(callee) && !keep.has(callee)) queue.push(callee);
|
||||
}
|
||||
}
|
||||
return keep;
|
||||
}
|
||||
|
||||
function convertAngleLikeToGlsl(code) {
|
||||
let s = code;
|
||||
const cut = s.indexOf("fragment ANGLE_FragmentOut");
|
||||
@@ -110,6 +139,14 @@ function convertAngleLikeToGlsl(code) {
|
||||
.replace(/\bANGLE_userUniforms\._uiTime\b/g, "iTime")
|
||||
.replace(/\bANGLE_userUniforms\._uiMouse\b/g, "iMouse")
|
||||
.replace(/\bANGLE_userUniforms\./g, "")
|
||||
.replace(/ANGLE_texture\(\s*ANGLE_textureEnvs\._uiChannel0\s*,\s*([^)]+)\)/g, "texture(iChannel0, $1)")
|
||||
.replace(/ANGLE_texture\(\s*ANGLE_textureEnvs\._uiChannel1\s*,\s*([^)]+)\)/g, "texture(iChannel1, $1)")
|
||||
.replace(/ANGLE_texture\(\s*ANGLE_textureEnvs\._uiChannel2\s*,\s*([^)]+)\)/g, "texture(iChannel2, $1)")
|
||||
.replace(/ANGLE_texture\(\s*ANGLE_textureEnvs\._uiChannel3\s*,\s*([^)]+)\)/g, "texture(iChannel3, $1)")
|
||||
.replace(/ANGLE_texelFetch\(\s*ANGLE_textureEnvs\._uiChannel0\s*,\s*([^,]+)\s*,\s*([^)]+)\)/g, "texelFetch(iChannel0, $1, $2)")
|
||||
.replace(/ANGLE_texelFetch\(\s*ANGLE_textureEnvs\._uiChannel1\s*,\s*([^,]+)\s*,\s*([^)]+)\)/g, "texelFetch(iChannel1, $1, $2)")
|
||||
.replace(/ANGLE_texelFetch\(\s*ANGLE_textureEnvs\._uiChannel2\s*,\s*([^,]+)\s*,\s*([^)]+)\)/g, "texelFetch(iChannel2, $1, $2)")
|
||||
.replace(/ANGLE_texelFetch\(\s*ANGLE_textureEnvs\._uiChannel3\s*,\s*([^,]+)\s*,\s*([^)]+)\)/g, "texelFetch(iChannel3, $1, $2)")
|
||||
.replace(/ANGLE_texture\([^)]*\)/g, "vec4(0.0)")
|
||||
.replace(/ANGLE_texelFetch\([^)]*\)/g, "vec4(0.0)")
|
||||
.replace(/\bANGLE_mod\(/g, "mod(")
|
||||
@@ -123,14 +160,21 @@ function convertAngleLikeToGlsl(code) {
|
||||
.replace(/\(\s*,/g, "(")
|
||||
.replace(/,\s*,/g, ",")
|
||||
.replace(/,\s*\)/g, ")")
|
||||
.replace(/\bANGLE_nonConstGlobals\./g, "")
|
||||
.replace(/(\d+\.\d+|\d+)f\b/g, "$1")
|
||||
.replace(/;\s*;/g, ";");
|
||||
|
||||
s = s.replace(/void\s+_umainImage\s*\([^)]*\)/g, "void _umainImage(out vec4 _ufragColor, vec2 _ufragCoord)");
|
||||
|
||||
const blocks = extractFunctionBlocks(s).filter((b) =>
|
||||
const fnBlocks = extractFunctionBlocks(s).filter((b) =>
|
||||
/(ANGLE_sc|ANGLE_sd|_u|mainImage|ANGLE_loopForwardProgress|_umainImage)/.test(b)
|
||||
);
|
||||
const fnMap = buildFunctionMap(fnBlocks);
|
||||
const keepNames = collectFunctionDependencies(fnMap, ["mainImage", "_umainImage"]);
|
||||
if (fnMap.has("ANGLE_loopForwardProgress")) keepNames.add("ANGLE_loopForwardProgress");
|
||||
const blocks = [...fnMap.entries()]
|
||||
.filter(([name]) => keepNames.has(name))
|
||||
.map(([, block]) => block);
|
||||
|
||||
if (!blocks.length || !s.includes("_umainImage")) return "";
|
||||
|
||||
@@ -153,6 +197,42 @@ function convertAngleLikeToGlsl(code) {
|
||||
return withoutDupLoop.join("\n\n");
|
||||
}
|
||||
|
||||
/** Strip ShaderToy export preamble (uniform iResolution…), fix `}uniform` gluing, keep first mainImage only. */
|
||||
function sanitizeShadertoyUserGlsl(code) {
|
||||
if (!code || typeof code !== "string") return "";
|
||||
let s = code.replace(/\r\n/g, "\n");
|
||||
s = s.replace(/\}\s*(?=uniform\s+)/g, "}\n");
|
||||
const lines = s.split("\n");
|
||||
s = lines
|
||||
.filter((line) => !/^\s*uniform\s+/.test(line.trim()))
|
||||
.join("\n");
|
||||
s = s.replace(/^\s+/, "").replace(/\s+$/, "");
|
||||
|
||||
const re = /\bvoid\s+mainImage\s*\(/g;
|
||||
const matches = [...s.matchAll(re)];
|
||||
if (matches.length <= 1) return s;
|
||||
|
||||
const start = matches[0].index;
|
||||
const braceStart = s.indexOf("{", s.indexOf("(", start));
|
||||
if (braceStart < 0) return s;
|
||||
let depth = 0;
|
||||
let i = braceStart;
|
||||
for (; i < s.length; i++) {
|
||||
const ch = s[i];
|
||||
if (ch === "{") depth++;
|
||||
else if (ch === "}") {
|
||||
depth--;
|
||||
if (depth === 0) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
const firstMain = s.slice(start, i);
|
||||
const prefix = s.slice(0, start).trimEnd();
|
||||
return (prefix ? `${prefix}\n\n` : "") + firstMain.trim();
|
||||
}
|
||||
|
||||
function normalizeIncomingCode(code) {
|
||||
if (!code || typeof code !== "string") return { error: "code 不能为空", normalized: "" };
|
||||
if (isAngleLike(code)) {
|
||||
@@ -162,10 +242,11 @@ function normalizeIncomingCode(code) {
|
||||
}
|
||||
return { error: "", normalized: converted };
|
||||
}
|
||||
if (!code.includes("mainImage")) {
|
||||
const sanitized = sanitizeShadertoyUserGlsl(code);
|
||||
if (!sanitized.includes("mainImage")) {
|
||||
return { error: "code 必须包含 mainImage", normalized: "" };
|
||||
}
|
||||
return { error: "", normalized: code };
|
||||
return { error: "", normalized: sanitized };
|
||||
}
|
||||
|
||||
async function autoNormalizeStoredShaders() {
|
||||
@@ -173,9 +254,10 @@ async function autoNormalizeStoredShaders() {
|
||||
let changed = false;
|
||||
const next = shaders.map((item) => {
|
||||
const code = String(item.code || "");
|
||||
if (isAngleLike(code) || code.includes("struct ANGLE_") || item.sourceFormat === "angle-metal-auto-converted") {
|
||||
const angle = isAngleLike(code) || code.includes("struct ANGLE_");
|
||||
if (angle) {
|
||||
const { error, normalized } = normalizeIncomingCode(code);
|
||||
if (!error && normalized) {
|
||||
if (!error && normalized && normalized !== code) {
|
||||
changed = true;
|
||||
return {
|
||||
...item,
|
||||
@@ -184,6 +266,18 @@ async function autoNormalizeStoredShaders() {
|
||||
sourceFormat: "angle-metal-auto-converted",
|
||||
};
|
||||
}
|
||||
return item;
|
||||
}
|
||||
const { error, normalized } = normalizeIncomingCode(code);
|
||||
if (!error && normalized && normalized !== code) {
|
||||
changed = true;
|
||||
const fixTag = item.sourceFormat === "angle-metal-auto-converted" ? { sourceFormat: "glsl" } : {};
|
||||
return {
|
||||
...item,
|
||||
code: normalized,
|
||||
updatedAt: new Date().toISOString(),
|
||||
...fixTag,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
@@ -195,10 +289,13 @@ app.get("/api/shaders", async (_req, res) => {
|
||||
let shaders = await readDb();
|
||||
// Runtime safeguard: always return normalized code for display layer.
|
||||
shaders = shaders.map((item) => {
|
||||
const { error, normalized } = normalizeIncomingCode(String(item.code || ""));
|
||||
const raw = String(item.code || "");
|
||||
const { error, normalized } = normalizeIncomingCode(raw);
|
||||
if (error || !normalized) return item;
|
||||
if (normalized === item.code) return item;
|
||||
return { ...item, code: normalized, sourceFormat: "angle-metal-auto-converted" };
|
||||
if (normalized === raw) return item;
|
||||
const next = { ...item, code: normalized };
|
||||
if (isAngleLike(raw)) next.sourceFormat = "angle-metal-auto-converted";
|
||||
return next;
|
||||
});
|
||||
res.json(shaders);
|
||||
} catch {
|
||||
@@ -250,8 +347,10 @@ app.delete("/api/shaders/:id", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
ensureDb().then(() => {
|
||||
autoNormalizeStoredShaders().catch(() => {});
|
||||
ensureDb().then(async () => {
|
||||
try {
|
||||
await autoNormalizeStoredShaders();
|
||||
} catch (_) {}
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Server running: http://localhost:${PORT}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user