11import { EOL } from 'os' ;
22import * as path from 'path' ;
33import * as fsSync from 'fs' ;
4- import { insertExtensionPreamble , fixupDirectives } from './preamble.js' ;
4+ import { insertExtensionPreamble , fixupDirectives , insertPreamble } from './preamble.js' ;
55import { argQuote , configureTools , getCachePath , launchTool , waitForToolBuffered } from './tools.js' ;
66import { checkMakeFolder , rmDir } from './download.js' ;
7- import { compressShader } from './minify.js' ;
7+ import { compressShader } from './minify.js' ;
8+ import * as crypto from 'crypto' ;
9+ import MagicString from 'magic-string' ;
810
911/**
1012 * @typedef {'vert'|'tesc'|'tese'|'geom'|'frag'|'comp' } GLSLStageName
1113 *
1214 * @typedef {Object } GLSLToolSharedOptions
15+ * @property {boolean } sourceMap
16+ * Emit source maps
1317 * @property {boolean } compress
1418 * Strip whitespace
1519 * @property {boolean } optimize
@@ -127,18 +131,32 @@ async function glslRunCross(name, workingDir, stageName, inputFile, input, emitL
127131 ] ) ;
128132}
129133
134+ /**
135+ * Generate unique build path
136+ * @param {string } id
137+ * @return {string }
138+ */
139+ function getBuildDir ( id ) {
140+ const sanitizeID = path . basename ( id ) . replace ( / ( [ ^ a - z 0 - 9 ] + ) / gi, '-' ) . toLowerCase ( ) ;
141+ const uniqID = ( ( Date . now ( ) >>> 0 ) + crypto . randomBytes ( 4 ) . readUInt32LE ( ) ) >>> 0 ; // +ve 4 byte unique ID
142+ const uniqIDHex = uniqID . toString ( 16 ) . padStart ( 8 , '0' ) ; // 8 char random hex
143+ return path . join ( getCachePath ( ) , 'glslBuild' , `${ sanitizeID } -${ uniqIDHex } ` ) ;
144+ }
145+
130146/**
131147 * @internal
132148 * @param {string } id File path
133149 * @param {string } source Source code
134150 * @param {GLSLStageName } stageName
135151 * @param {Partial<GLSLToolOptions> } [glslOptions]
136152 * @param {(message: string) => void } [errorLog]
153+ * @return {Promise<import('rollup').SourceDescription> }
137154 */
138155export async function glslProcessSource ( id , source , stageName , glslOptions = { } , errorLog = console . error ) {
139156
140157 /** @type {GLSLToolOptions } */
141158 const options = {
159+ sourceMap : true ,
142160 compress : true ,
143161 optimize : true ,
144162 emitLineDirectives : false ,
@@ -157,7 +175,7 @@ export async function glslProcessSource(id, source, stageName, glslOptions = {},
157175
158176 let tempBuildDir ;
159177 if ( options . optimize ) {
160- tempBuildDir = path . join ( getCachePath ( ) , 'glslBuild' ) ;
178+ tempBuildDir = getBuildDir ( id ) ;
161179 rmDir ( tempBuildDir ) ;
162180 checkMakeFolder ( tempBuildDir ) ;
163181 }
@@ -209,7 +227,7 @@ export async function glslProcessSource(id, source, stageName, glslOptions = {},
209227 ...options . extraValidatorParams ,
210228 ] ;
211229
212- let outputGLSL ;
230+ let processedGLSL ;
213231
214232 if ( options . optimize ) {
215233 const outputBuild = await glslRunValidator ( 'Build spirv' , targetDir , stageName ,
@@ -233,11 +251,11 @@ export async function glslProcessSource(id, source, stageName, glslOptions = {},
233251 // '--print-all', // Print spirv for debugging
234252 ] , options . extraOptimizerParams ) ;
235253 if ( ! fsSync . existsSync ( optimizedFileAbs ) ) {
236- throw new Error ( `Optimize spirv failed: no output file` ) ;
254+ throw new Error ( `Optimize spirv failed: no output file ( ${ optimizedFileAbs } ) ` ) ;
237255 }
238256 }
239257
240- outputGLSL = await glslRunCross ( 'Build spirv to GLSL' , targetDir , stageName ,
258+ processedGLSL = await glslRunCross ( 'Build spirv to GLSL' , targetDir , stageName ,
241259 options . optimizerDebugSkipOptimizer ? outputFileAbs : optimizedFileAbs , undefined , options . emitLineDirectives , [
242260 '--es' , // WebGL is always ES
243261 '--version' , `${ targetGlslVersion } ` ,
@@ -252,22 +270,42 @@ export async function glslProcessSource(id, source, stageName, glslOptions = {},
252270
253271
254272 } else {
255- outputGLSL = await glslRunValidator ( 'Preprocessing' , targetDir , stageName , code , [
273+ processedGLSL = await glslRunValidator ( 'Preprocessing' , targetDir , stageName , code , [
256274 '-E' , // print pre-processed GLSL
257275 ] , extraValidatorParams ) ;
258276 const outputValidated = await glslRunValidator ( 'Validation' , targetDir , stageName ,
259- outputGLSL , [ ] , extraValidatorParams ) ;
277+ processedGLSL , [ ] , extraValidatorParams ) ;
260278 }
261279
262- outputGLSL = fixupDirectives ( outputGLSL ,
280+ processedGLSL = fixupDirectives ( processedGLSL ,
263281 options . emitLineDirectives && ! options . suppressLineExtensionDirective ,
264282 didInsertion && ( ! options . optimize || options . emitLineDirectives ) ,
265283 options . optimize , ! options . emitLineDirectives , undefined ) ;
266284
267- if ( options . compress ) {
268- outputGLSL = compressShader ( outputGLSL ) ;
285+
286+ const outputCode = options . compress ? compressShader ( processedGLSL ) : processedGLSL ;
287+
288+ /** @type {import('rollup').LoadResult } */
289+ const result = {
290+ code : outputCode ,
291+ map : { mappings : '' } ,
292+ } ;
293+
294+ if ( options . sourceMap ) {
295+ const sourceMapSource = insertPreamble ( processedGLSL ,
296+ '/*\n' +
297+ `* Preprocessed${ options . optimize ?' + Optimized' :'' } from '${ targetID } '\n` +
298+ ( options . compress ? '* [Embedded string is compressed]\n' :'' ) +
299+ '*/'
300+ ) . code ;
301+ const magicString = new MagicString ( sourceMapSource ) ;
302+ result . map = magicString . generateMap ( {
303+ source : id ,
304+ includeContent : true ,
305+ hires : true ,
306+ } ) ;
269307 }
270308
271- return outputGLSL ;
309+ return result ;
272310
273311}
0 commit comments