@@ -57,23 +57,78 @@ class WebgpuDebug {
5757 * End the previous error scope, and print errors if any.
5858 *
5959 * @param {WebgpuGraphicsDevice } device - The graphics device.
60+ * @param {string } label - The label for the error scope.
6061 * @param {...any } args - Additional parameters that form the error message.
6162 */
62- static end ( device , ...args ) {
63+ static async end ( device , label , ...args ) {
6364 const header = WebgpuDebug . _scopes . pop ( ) ;
6465 const marker = WebgpuDebug . _markers . pop ( ) ;
6566 Debug . assert ( header , 'Non matching end.' ) ;
6667
67- device . wgpu . popErrorScope ( ) . then ( ( error ) => {
68- if ( error ) {
69- const count = WebgpuDebug . _loggedMessages . get ( error . message ) ?? 0 ;
70- if ( count < MAX_DUPLICATES ) {
71- const tooMany = count === MAX_DUPLICATES - 1 ? ' (Too many errors, ignoring this one from now)' : '' ;
72- WebgpuDebug . _loggedMessages . set ( error . message , count + 1 ) ;
73- console . error ( `WebGPU ${ header } error: ${ error . message } ` , tooMany , 'while rendering' , marker , ...args ) ;
74- }
68+ const error = await device . wgpu . popErrorScope ( ) ;
69+ if ( error ) {
70+ const count = WebgpuDebug . _loggedMessages . get ( error . message ) ?? 0 ;
71+ if ( count < MAX_DUPLICATES ) {
72+ const tooMany = count === MAX_DUPLICATES - 1 ? ' (Too many errors, ignoring this one from now)' : '' ;
73+ WebgpuDebug . _loggedMessages . set ( error . message , count + 1 ) ;
74+ console . error ( `WebGPU ${ label } ${ header } error: ${ error . message } ` , tooMany , 'while rendering' , marker , ...args ) ;
7575 }
76- } ) ;
76+ }
77+ }
78+
79+ /**
80+ * Ends the shader validation scope by retrieving and logging any compilation errors
81+ * or warnings from the shader module. Also handles WebGPU validation errors, while
82+ * avoiding duplicate error messages.
83+ *
84+ * @param {WebgpuGraphicsDevice } device - The WebGPU graphics device.
85+ * @param {GPUShaderModule } shaderModule - The compiled WebGPU shader module.
86+ * @param {string } source - The original shader source code.
87+ * @param {number } [contextLines] - The number of lines before and after the error to log.
88+ * @param {...any } args - Additional parameters providing context about the shader.
89+ */
90+ static async endShader ( device , shaderModule , source , contextLines = 2 , ...args ) {
91+ const header = WebgpuDebug . _scopes . pop ( ) ;
92+ const marker = WebgpuDebug . _markers . pop ( ) ;
93+ Debug . assert ( header , 'Non-matching error scope end.' ) ;
94+
95+ // Capture popErrorScope error (if any)
96+ const error = await device . wgpu . popErrorScope ( ) ;
97+ let errorMessage = '' ;
98+
99+ if ( error ) {
100+ errorMessage += `WebGPU ShaderModule creation ${ header } error: ${ error . message } ` ;
101+ errorMessage += ` - While rendering ${ marker } \n` ;
102+ }
103+
104+ // Get shader compilation errors
105+ const compilationInfo = await shaderModule . getCompilationInfo ( ) ;
106+
107+ if ( compilationInfo . messages . length > 0 ) {
108+ // split source into lines
109+ const sourceLines = source . split ( '\n' ) ;
110+
111+ compilationInfo . messages . forEach ( ( message , index ) => {
112+ const { type, lineNum, linePos, message : msg } = message ;
113+ const lineIndex = lineNum - 1 ; // Convert to zero-based index
114+
115+ errorMessage += `\n----- ${ type . toUpperCase ( ) } ${ index + 1 } context: :${ lineNum } :${ linePos } ${ type } : ${ msg } \n` ;
116+
117+ // Extract surrounding lines for context
118+ const startLine = Math . max ( 0 , lineIndex - contextLines ) ;
119+ const endLine = Math . min ( sourceLines . length , lineIndex + contextLines + 1 ) ;
120+
121+ for ( let i = startLine ; i < endLine ; i ++ ) {
122+ const linePrefix = i === lineIndex ? '> ' : ' ' ;
123+ errorMessage += `${ linePrefix } ${ i + 1 } : ${ sourceLines [ i ] } \n` ;
124+ }
125+ } ) ;
126+ }
127+
128+ // only log if there are errors or messages
129+ if ( errorMessage ) {
130+ console . error ( errorMessage , ...args ) ;
131+ }
77132 }
78133}
79134
0 commit comments