@luma.gl/shadertools
Version:
Shader module system for luma.gl
151 lines • 6.71 kB
JavaScript
// luma.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
export const WGSL_BINDABLE_VARIABLE_PATTERN = '(?:var<\\s*(uniform|storage(?:\\s*,\\s*[A-Za-z_][A-Za-z0-9_]*)?)\\s*>|var)\\s+([A-Za-z_][A-Za-z0-9_]*)';
const WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN = '\\s*';
export const MODULE_WGSL_BINDING_DECLARATION_REGEXES = [
new RegExp(`@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`, 'g'),
new RegExp(`@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`, 'g')
];
export const WGSL_BINDING_DECLARATION_REGEXES = [
new RegExp(`@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`, 'g'),
new RegExp(`@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@binding\\(\\s*(auto|\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`, 'g')
];
export const WGSL_EXPLICIT_BINDING_DECLARATION_REGEXES = [
new RegExp(`@binding\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`, 'g'),
new RegExp(`@group\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}@binding\\(\\s*(\\d+)\\s*\\)${WGSL_BINDING_DECLARATION_SEPARATOR_PATTERN}${WGSL_BINDABLE_VARIABLE_PATTERN}`, 'g')
];
const WGSL_AUTO_BINDING_DECLARATION_REGEXES = [
new RegExp(`@binding\\(\\s*(auto)\\s*\\)\\s*@group\\(\\s*(\\d+)\\s*\\)\\s*${WGSL_BINDABLE_VARIABLE_PATTERN}`, 'g'),
new RegExp(`@group\\(\\s*(\\d+)\\s*\\)\\s*@binding\\(\\s*(auto)\\s*\\)\\s*${WGSL_BINDABLE_VARIABLE_PATTERN}`, 'g'),
new RegExp(`@binding\\(\\s*(auto)\\s*\\)\\s*@group\\(\\s*(\\d+)\\s*\\)(?:[\\s\\n\\r]*@[A-Za-z_][^\\n\\r]*)*[\\s\\n\\r]*${WGSL_BINDABLE_VARIABLE_PATTERN}`, 'g'),
new RegExp(`@group\\(\\s*(\\d+)\\s*\\)\\s*@binding\\(\\s*(auto)\\s*\\)(?:[\\s\\n\\r]*@[A-Za-z_][^\\n\\r]*)*[\\s\\n\\r]*${WGSL_BINDABLE_VARIABLE_PATTERN}`, 'g')
];
export function maskWGSLComments(source) {
const maskedCharacters = source.split('');
let index = 0;
let blockCommentDepth = 0;
let inLineComment = false;
let inString = false;
let isEscaped = false;
while (index < source.length) {
const character = source[index];
const nextCharacter = source[index + 1];
if (inString) {
if (isEscaped) {
isEscaped = false;
}
else if (character === '\\') {
isEscaped = true;
}
else if (character === '"') {
inString = false;
}
index++;
continue;
}
if (inLineComment) {
if (character === '\n' || character === '\r') {
inLineComment = false;
}
else {
maskedCharacters[index] = ' ';
}
index++;
continue;
}
if (blockCommentDepth > 0) {
if (character === '/' && nextCharacter === '*') {
maskedCharacters[index] = ' ';
maskedCharacters[index + 1] = ' ';
blockCommentDepth++;
index += 2;
continue;
}
if (character === '*' && nextCharacter === '/') {
maskedCharacters[index] = ' ';
maskedCharacters[index + 1] = ' ';
blockCommentDepth--;
index += 2;
continue;
}
if (character !== '\n' && character !== '\r') {
maskedCharacters[index] = ' ';
}
index++;
continue;
}
if (character === '"') {
inString = true;
index++;
continue;
}
if (character === '/' && nextCharacter === '/') {
maskedCharacters[index] = ' ';
maskedCharacters[index + 1] = ' ';
inLineComment = true;
index += 2;
continue;
}
if (character === '/' && nextCharacter === '*') {
maskedCharacters[index] = ' ';
maskedCharacters[index + 1] = ' ';
blockCommentDepth = 1;
index += 2;
continue;
}
index++;
}
return maskedCharacters.join('');
}
export function getWGSLBindingDeclarationMatches(source, regexes) {
const maskedSource = maskWGSLComments(source);
const matches = [];
for (const regex of regexes) {
regex.lastIndex = 0;
let match;
match = regex.exec(maskedSource);
while (match) {
const isBindingFirst = regex === regexes[0];
const index = match.index;
const length = match[0].length;
matches.push({
match: source.slice(index, index + length),
index,
length,
bindingToken: match[isBindingFirst ? 1 : 2],
groupToken: match[isBindingFirst ? 2 : 1],
accessDeclaration: match[3]?.trim(),
name: match[4]
});
match = regex.exec(maskedSource);
}
}
return matches.sort((left, right) => left.index - right.index);
}
export function replaceWGSLBindingDeclarationMatches(source, regexes, replacer) {
const matches = getWGSLBindingDeclarationMatches(source, regexes);
if (!matches.length) {
return source;
}
let relocatedSource = '';
let lastIndex = 0;
for (const match of matches) {
relocatedSource += source.slice(lastIndex, match.index);
relocatedSource += replacer(match);
lastIndex = match.index + match.length;
}
relocatedSource += source.slice(lastIndex);
return relocatedSource;
}
export function hasWGSLAutoBinding(source) {
return /@binding\(\s*auto\s*\)/.test(maskWGSLComments(source));
}
export function getFirstWGSLAutoBindingDeclarationMatch(source, regexes) {
const autoBindingRegexes = regexes === MODULE_WGSL_BINDING_DECLARATION_REGEXES ||
regexes === WGSL_BINDING_DECLARATION_REGEXES
? WGSL_AUTO_BINDING_DECLARATION_REGEXES
: regexes;
return getWGSLBindingDeclarationMatches(source, autoBindingRegexes).find(declarationMatch => declarationMatch.bindingToken === 'auto');
}
//# sourceMappingURL=wgsl-binding-scan.js.map