@@ -1180,6 +1180,205 @@ float ia_notEqual(IntervalResult a, IntervalResult b) {
11801180}
11811181` ;
11821182
1183+ // ---------------------------------------------------------------------------
1184+ // Selective preamble builder
1185+ //
1186+ // Parses the monolithic GLSL_INTERVAL_LIBRARY into individual function blocks
1187+ // with auto-detected dependencies. At compile time, only the functions
1188+ // referenced by the compiled expression (plus their transitive dependencies)
1189+ // are emitted.
1190+ // ---------------------------------------------------------------------------
1191+
1192+ interface GLSLFunctionBlock {
1193+ /** The function name (e.g. "ia_sin") */
1194+ name : string ;
1195+ /** Full source text of the function (including preceding comment lines) */
1196+ source : string ;
1197+ /** Names of other ia_ or _gpu_ functions called from this function's body */
1198+ deps : string [ ] ;
1199+ }
1200+
1201+ /** Header: constants, struct, epsilon — always emitted */
1202+ let _preambleHeader = '' ;
1203+ /** Mid-preamble constant blocks (e.g. IA_TRUE/FALSE/MAYBE) keyed by marker */
1204+ const _preambleConstants : Map < string , string > = new Map ( ) ;
1205+ /** Individual function blocks keyed by function name */
1206+ const _preambleFunctions : Map < string , GLSLFunctionBlock > = new Map ( ) ;
1207+ /** Set to true once parsing is done */
1208+ let _preambleParsed = false ;
1209+
1210+ /** Regex matching function declarations in the GLSL preamble */
1211+ const GLSL_FUNC_RE =
1212+ / ^ ( I n t e r v a l R e s u l t | v e c 2 | f l o a t | b o o l | v o i d ) \s + ( i a _ \w + | _ g p u _ \w + ) \s * \( / ;
1213+
1214+ /** Regex to find calls to ia_ or _gpu_ functions within a body */
1215+ const GLSL_CALL_RE = / \b ( i a _ \w + | _ g p u _ \w + ) \s * \( / g;
1216+
1217+ /** Regex for constant declarations like `const float IA_TRUE = 1.0;` */
1218+ const GLSL_CONST_RE = / ^ c o n s t \s + f l o a t \s + ( I A _ \w + ) \s * = / ;
1219+
1220+ function parsePreamble ( ) : void {
1221+ if ( _preambleParsed ) return ;
1222+ _preambleParsed = true ;
1223+
1224+ const lines = GLSL_INTERVAL_LIBRARY . split ( '\n' ) ;
1225+ let headerDone = false ;
1226+ const headerLines : string [ ] = [ ] ;
1227+ let currentBlock : string [ ] = [ ] ;
1228+ let currentName : string | null = null ;
1229+ let braceDepth = 0 ;
1230+ let inFunction = false ;
1231+ let pendingComments : string [ ] = [ ] ;
1232+ let pendingConstants : string [ ] = [ ] ;
1233+
1234+ for ( const line of lines ) {
1235+ // Check if this is a constant declaration outside a function
1236+ const constMatch = ! inFunction && GLSL_CONST_RE . exec ( line ) ;
1237+ if ( constMatch && headerDone ) {
1238+ // Mid-preamble constant (e.g. IA_TRUE) — accumulate
1239+ pendingConstants . push ( line ) ;
1240+ continue ;
1241+ }
1242+
1243+ const funcMatch = ! inFunction && GLSL_FUNC_RE . exec ( line ) ;
1244+
1245+ if ( funcMatch ) {
1246+ if ( ! headerDone ) {
1247+ _preambleHeader = headerLines . join ( '\n' ) ;
1248+ headerDone = true ;
1249+ }
1250+
1251+ currentName = funcMatch [ 2 ] ;
1252+ currentBlock = [ ...pendingComments , ...pendingConstants , line ] ;
1253+ pendingComments = [ ] ;
1254+ pendingConstants = [ ] ;
1255+ inFunction = true ;
1256+ braceDepth = 0 ;
1257+
1258+ // Count braces on this line
1259+ for ( const ch of line ) {
1260+ if ( ch === '{' ) braceDepth ++ ;
1261+ if ( ch === '}' ) braceDepth -- ;
1262+ }
1263+ if ( braceDepth <= 0 ) {
1264+ // Single-line function
1265+ finishFunction ( currentName , currentBlock . join ( '\n' ) ) ;
1266+ inFunction = false ;
1267+ currentName = null ;
1268+ }
1269+ } else if ( inFunction ) {
1270+ currentBlock . push ( line ) ;
1271+ for ( const ch of line ) {
1272+ if ( ch === '{' ) braceDepth ++ ;
1273+ if ( ch === '}' ) braceDepth -- ;
1274+ }
1275+ if ( braceDepth <= 0 ) {
1276+ finishFunction ( currentName ! , currentBlock . join ( '\n' ) ) ;
1277+ inFunction = false ;
1278+ currentName = null ;
1279+ }
1280+ } else if ( ! headerDone ) {
1281+ headerLines . push ( line ) ;
1282+ } else {
1283+ // Between functions — could be comment or blank
1284+ const trimmed = line . trim ( ) ;
1285+ if ( trimmed . startsWith ( '//' ) || trimmed === '' ) {
1286+ pendingComments . push ( line ) ;
1287+ }
1288+ }
1289+ }
1290+
1291+ // Flush any remaining pending constants into the header
1292+ if ( pendingConstants . length > 0 ) {
1293+ _preambleHeader += '\n' + pendingConstants . join ( '\n' ) ;
1294+ }
1295+ }
1296+
1297+ function finishFunction ( name : string , source : string ) : void {
1298+ // Auto-detect dependencies by scanning the body for ia_*/_gpu_* calls
1299+ const deps = new Set < string > ( ) ;
1300+ let match : RegExpExecArray | null ;
1301+ const callRe = new RegExp ( GLSL_CALL_RE . source , 'g' ) ;
1302+ while ( ( match = callRe . exec ( source ) ) !== null ) {
1303+ const callee = match [ 1 ] ;
1304+ if ( callee !== name ) deps . add ( callee ) ;
1305+ }
1306+
1307+ // GLSL overloads: the IntervalResult overload of ia_sin calls ia_sin(vec2)
1308+ // Since they share a name, we don't add self-references as deps.
1309+ // But we do need to ensure all overloads of a function are emitted together.
1310+
1311+ const existing = _preambleFunctions . get ( name ) ;
1312+ if ( existing ) {
1313+ // Multiple overloads — merge source and deps
1314+ existing . source += '\n\n' + source ;
1315+ for ( const d of deps ) existing . deps . push ( d ) ;
1316+ } else {
1317+ _preambleFunctions . set ( name , {
1318+ name,
1319+ source,
1320+ deps : [ ...deps ] ,
1321+ } ) ;
1322+ }
1323+ }
1324+
1325+ /**
1326+ * Build a minimal interval GLSL preamble containing only the functions
1327+ * that the compiled code actually uses (plus transitive dependencies).
1328+ */
1329+ function buildIntervalPreamble ( code : string ) : string {
1330+ parsePreamble ( ) ;
1331+
1332+ // 1. Find all ia_*/_gpu_* calls in the compiled expression code
1333+ const needed = new Set < string > ( ) ;
1334+ let match : RegExpExecArray | null ;
1335+ const callRe = new RegExp ( GLSL_CALL_RE . source , 'g' ) ;
1336+ while ( ( match = callRe . exec ( code ) ) !== null ) {
1337+ needed . add ( match [ 1 ] ) ;
1338+ }
1339+
1340+ if ( needed . size === 0 ) return _preambleHeader ;
1341+
1342+ // 2. Resolve transitive dependencies
1343+ const resolved = new Set < string > ( ) ;
1344+ function resolve ( name : string ) : void {
1345+ if ( resolved . has ( name ) ) return ;
1346+ const block = _preambleFunctions . get ( name ) ;
1347+ if ( ! block ) return ;
1348+ for ( const dep of block . deps ) resolve ( dep ) ;
1349+ resolved . add ( name ) ;
1350+ }
1351+ for ( const name of needed ) resolve ( name ) ;
1352+
1353+ // 3. Emit in dependency order (resolved set preserves insertion order,
1354+ // and deps are resolved before dependents)
1355+ const parts : string [ ] = [ _preambleHeader ] ;
1356+
1357+ // Check if any comparison functions are needed — if so, include IA_TRUE/FALSE/MAYBE
1358+ const needsComparisonConstants = [ ...resolved ] . some (
1359+ ( name ) =>
1360+ name . startsWith ( 'ia_less' ) ||
1361+ name . startsWith ( 'ia_greater' ) ||
1362+ name . startsWith ( 'ia_equal' ) ||
1363+ name . startsWith ( 'ia_notEqual' ) ||
1364+ name === 'ia_and' ||
1365+ name === 'ia_or' ||
1366+ name === 'ia_not'
1367+ ) ;
1368+ if ( needsComparisonConstants ) {
1369+ parts . push (
1370+ '\nconst float IA_TRUE = 1.0;\nconst float IA_FALSE = 0.0;\nconst float IA_MAYBE = 0.5;'
1371+ ) ;
1372+ }
1373+
1374+ for ( const name of resolved ) {
1375+ const block = _preambleFunctions . get ( name ) ;
1376+ if ( block ) parts . push ( '\n' + block . source ) ;
1377+ }
1378+
1379+ return parts . join ( '\n' ) ;
1380+ }
1381+
11831382/**
11841383 * GLSL interval operators - all become function calls
11851384 */
@@ -1450,7 +1649,7 @@ export class IntervalGLSLTarget implements LanguageTarget<Expression> {
14501649 target : 'interval-glsl' ,
14511650 success : true ,
14521651 code : glslCode ,
1453- preamble : GLSL_INTERVAL_LIBRARY ,
1652+ preamble : buildIntervalPreamble ( glslCode ) ,
14541653 } ;
14551654 }
14561655
@@ -1514,7 +1713,7 @@ export class IntervalGLSLTarget implements LanguageTarget<Expression> {
15141713 return `#version ${ version }
15151714precision highp float;
15161715
1517- ${ GLSL_INTERVAL_LIBRARY }
1716+ ${ buildIntervalPreamble ( body ) }
15181717
15191718IntervalResult ${ functionName } (${ params } ) {
15201719 return ${ body } ;
0 commit comments