{"author_link":"\/users\/eckkert","author_name":"Eckkert","author_uid":"eckkert","comments":[],"epoch":1617426534,"event":"LD48","format":"md","ldjam_node_id":235995,"likes":7,"metadata":{"p_key":"152435","p_author":"Eckkert","p_authorkey":"1186175","p_urlkey":"369356","p_title":"Use Our Code #1: Better Errors for Shader Compilation","p_cat":"LDJam ","p_event":"LD48","p_time":"1617426534","p_likes":"7","p_comments":"0","p_status":"WAYBACK","us_key":"1186175","us_name":"Eckkert","us_username":"eckkert","event_start":"1619222400","event_key":"109","event_name":"Ludum Dare 48"},"node":{"_collation":{"body_sanitizer":"TextUtils::SanitizeHTML via existing importer","event":"LD48","removed_author":false},"_superparent":233335,"_trust":3,"author":186175,"body":"Our team is using WebGL and Javascript for this jam, and to that end I am writing an improved version of [the custom engine we used last time](https:\/\/github.com\/Atomotron\/ld47-prep). The spirit of LudumDare is about collaboration and sharing, and in that spirit, we invite you to use our code!\n\nFirst off, here is some code to pretty-print the content of `gl.getShaderInfoLog`. Why's it so great to pretty-print compilation errors? Well, the default messages I get from the browser are bare oneliners like `ERROR: 0:10: 'x' : syntax error`. Last jam, I had to hunt through the line numbers manually, which was inconvenient when the shader source was burred inside a script tag in an HTML file. Having learned that this was a problem, today I wrote a simple regex parser that prints much more helpful messages, like this:\n\n```\n==============================================================================\nWhen compiling vertex shader \"a\":\n\n    gl_Position = vec4(vertex,0.0,1.0); \n    world_coord = (inverse_view * vec3(vertex,1.0)).xy \u25c0\u25c0\u25c0 MISSING SOMETHING?\n    daytime = dot(world_coord,solar_vector);\n    \u2580\u2580\u2580\u2580\u2580\u2580\u2580\nERROR: 0:10: 'daytime' : syntax error```\nIt's even so nice as to try and find the line where the semicolon is missing. (Heuristically of course, I am not parsing GLSL.)\n\nIf you think that's cool, well, hopefully your driver\/browser\/whatever produces raw errors that textually look like my computer's. If so, then the following code may help you:\n\n```javascript\n\/\/ WebGL shader compilation errors don't provide a lot of context.\n\/\/ This pretty-printer extracts line numbers from the message, and\n\/\/ formats a helpful report on the site of the issue.\n\/\/ My driver can return several errors on several lines, so first let's split them.\nfunction prettyPrintShaderErrors(name,source,message) {\n    const errors = message.split(\/\\r?\\n\/);\n    const readouts = [];\n    for (const error of errors) {\n        if (error.length == 0) continue;\n        readouts.push(prettyPrintShaderError(name,source,error));\n    }\n    return readouts.join(\"\\n\");\n}\n\n\/\/ Pretty-print a single error.\nfunction prettyPrintShaderError(name,source,error) {\n    const lowEffortMessage = `When compiling ${name}: ${error}`;\n    const lines = source.split(\/\\r?\\n\/);\n    \/\/ An OpenGL compilation error will look like:\n    \/\/  \"ERROR: 0:11: 'daytime' : syntax error\"\n    \/\/ So, the first thing we do is split at the :\n    const errorParts = error.split(\":\"); \n    if (errorParts[0] !== \"ERROR\" || errorParts.length < 3) { \n        \/\/ Give up if it doesn't look like we're expecting.\n        return lowEffortMessage;\n    }\n    const [part,line] = [\n        parseInt(errorParts[1],10),\n        parseInt(errorParts[2],10) - 1 \/\/ OpenGL starts at line 1\n    ];\n    if (part !== 0) return lowEffortMessage; \/\/ 'part' is an OpenGL thing that webGL shouldn't have. If it isn't zero, then we aren't properly parsing the error.\n    if (line >= lines.length) return lowEffortMessage;\n    \/\/ Attempt to find the error-triggering string in the bad line\n    \/\/ Strip whitespace and wrapping quotes\n    const triggering = errorParts[3].replace(\/^\\s+['|\"]|['|\"]\\s+$\/g, '');\n    const triggering_index = lines[line].search(triggering);\n    \/\/ Probe for missing semicolons, a common error.\n    \/\/ This regex-based heuristic is NOT PERFECT, but it can work sometimes.\n    let semicolon_missing_at = null;\n    \/\/ Semicolons, { and } can all go before a statement.\n    const goodLine = \/[;|{|}]\\s*(\\\/\\\/.*)?$\/; \n    const emptyLine = \/^\\s*(\\\/\\\/.*)?$\/;\n    for (let i=line-1; i>=0; --i) {\n        if (goodLine.test(lines[i])) break; \/\/ We found line that terminates right.\n        if (!emptyLine.test(lines[i])) { \/\/ If the line has stuff on it...\n            semicolon_missing_at = i; \/\/ then since we haven't found a good one...\n            break; \/\/ it must be a bad one. We're done!\n        }\n    }\n    \/\/ Decide whether or not we suspect a missing semicolon\/brace\n    let suspected_missing_semicolon = false;\n    if (semicolon_missing_at !== null && triggering_index >= 0) {\n        \/\/ If the triggering string appears after nothing but whitespace\n        if (\/\\s*\/.test(lines[line].slice(0,triggering_index))) {\n            suspected_missing_semicolon = true;\n        }\n    }\n    \/\/ Select context for error from source lines\n    const context_end = line+1; \/\/ Our context must include the triggering line!\n    const context_start = context_end - 3; \/\/ 3 lines of context by default\n    if (semicolon_missing_at !== null && context_start > semicolon_missing_at) {\n        context_start = semicolon_missing_at; \/\/ Always include the suspected line\n    }\n    if (context_start <= 0) context_start = 0;\n    const context = lines.slice(context_start,context_end);\n    \/\/ Assemble the message\n    if (suspected_missing_semicolon) {\n        const loc_in_context = semicolon_missing_at - context_start;\n        context[loc_in_context] += \" \u25c0\u25c0\u25c0 MISSING SOMETHING?\";\n    }\n    const message = [`When compiling ${name}:\\n`].concat(context);\n    if (triggering_index >= 0) {\n        message.push(' '.repeat(triggering_index) + '\u2580'.repeat(triggering.length));\n    }\n    message.push(error);\n    let longest = 0;\n    for (const l of message) if (l.length > longest) longest = l.length;\n    message.unshift('='.repeat(longest+1));\n    return message.join('\\n');\n}```","comments":1,"comments-timestamp":"2021-04-10T09:38:51Z","created":"2021-04-03T04:47:56Z","files":[],"files-timestamp":0,"id":235995,"love":7,"love-timestamp":"2021-04-06T01:27:15Z","meta":[],"modified":"2021-04-10T09:38:51Z","name":"Use Our Code #1: Better Errors for Shader Compilation","node-timestamp":"2021-04-06T03:13:37Z","parent":235994,"parents":[1,5,9,233335,235994],"path":"\/events\/ludum-dare\/48\/inner-pieces\/use-our-code-1-shader-compiler-with-better-errors","published":"2021-04-03T05:08:54Z","scope":"public","slug":"use-our-code-1-shader-compiler-with-better-errors","subsubtype":"","subtype":"","type":"post","version":716748},"node_metadata":{"n_key":"235995","n_urlkey":"369356","n_parent":"235994","n_path":"\/events\/ludum-dare\/48\/inner-pieces\/use-our-code-1-shader-compiler-with-better-errors","n_slug":"use-our-code-1-shader-compiler-w","n_type":"post","n_subtype":"","n_subsubtype":"","n_author":"186175","n_created":"1617425276","n_modified":"1618047531","n_version":"716748","n_status":"WAYBACK"},"source_url":"https:\/\/ldjam.com\/events\/ludum-dare\/48\/inner-pieces\/use-our-code-1-shader-compiler-with-better-errors","text":"Our team is using WebGL and Javascript for this jam, and to that end I am writing an improved version of [the custom engine we used last time](https:\/\/github.com\/Atomotron\/ld47-prep). The spirit of LudumDare is about collaboration and sharing, and in that spirit, we invite you to use our code!\n\nFirst off, here is some code to pretty-print the content of `gl.getShaderInfoLog`. Why's it so great to pretty-print compilation errors? Well, the default messages I get from the browser are bare oneliners like `ERROR: 0:10: 'x' : syntax error`. Last jam, I had to hunt through the line numbers manually, which was inconvenient when the shader source was burred inside a script tag in an HTML file. Having learned that this was a problem, today I wrote a simple regex parser that prints much more helpful messages, like this:\n\n```\n==============================================================================\nWhen compiling vertex shader \"a\":\n\n    gl_Position = vec4(vertex,0.0,1.0); \n    world_coord = (inverse_view * vec3(vertex,1.0)).xy \u25c0\u25c0\u25c0 MISSING SOMETHING?\n    daytime = dot(world_coord,solar_vector);\n    \u2580\u2580\u2580\u2580\u2580\u2580\u2580\nERROR: 0:10: 'daytime' : syntax error```\nIt's even so nice as to try and find the line where the semicolon is missing. (Heuristically of course, I am not parsing GLSL.)\n\nIf you think that's cool, well, hopefully your driver\/browser\/whatever produces raw errors that textually look like my computer's. If so, then the following code may help you:\n\n```javascript\n\/\/ WebGL shader compilation errors don't provide a lot of context.\n\/\/ This pretty-printer extracts line numbers from the message, and\n\/\/ formats a helpful report on the site of the issue.\n\/\/ My driver can return several errors on several lines, so first let's split them.\nfunction prettyPrintShaderErrors(name,source,message) {\n    const errors = message.split(\/\\r?\\n\/);\n    const readouts = [];\n    for (const error of errors) {\n        if (error.length == 0) continue;\n        readouts.push(prettyPrintShaderError(name,source,error));\n    }\n    return readouts.join(\"\\n\");\n}\n\n\/\/ Pretty-print a single error.\nfunction prettyPrintShaderError(name,source,error) {\n    const lowEffortMessage = `When compiling ${name}: ${error}`;\n    const lines = source.split(\/\\r?\\n\/);\n    \/\/ An OpenGL compilation error will look like:\n    \/\/  \"ERROR: 0:11: 'daytime' : syntax error\"\n    \/\/ So, the first thing we do is split at the :\n    const errorParts = error.split(\":\"); \n    if (errorParts[0] !== \"ERROR\" || errorParts.length < 3) { \n        \/\/ Give up if it doesn't look like we're expecting.\n        return lowEffortMessage;\n    }\n    const [part,line] = [\n        parseInt(errorParts[1],10),\n        parseInt(errorParts[2],10) - 1 \/\/ OpenGL starts at line 1\n    ];\n    if (part !== 0) return lowEffortMessage; \/\/ 'part' is an OpenGL thing that webGL shouldn't have. If it isn't zero, then we aren't properly parsing the error.\n    if (line >= lines.length) return lowEffortMessage;\n    \/\/ Attempt to find the error-triggering string in the bad line\n    \/\/ Strip whitespace and wrapping quotes\n    const triggering = errorParts[3].replace(\/^\\s+['|\"]|['|\"]\\s+$\/g, '');\n    const triggering_index = lines[line].search(triggering);\n    \/\/ Probe for missing semicolons, a common error.\n    \/\/ This regex-based heuristic is NOT PERFECT, but it can work sometimes.\n    let semicolon_missing_at = null;\n    \/\/ Semicolons, { and } can all go before a statement.\n    const goodLine = \/[;|{|}]\\s*(\\\/\\\/.*)?$\/; \n    const emptyLine = \/^\\s*(\\\/\\\/.*)?$\/;\n    for (let i=line-1; i>=0; --i) {\n        if (goodLine.test(lines[i])) break; \/\/ We found line that terminates right.\n        if (!emptyLine.test(lines[i])) { \/\/ If the line has stuff on it...\n            semicolon_missing_at = i; \/\/ then since we haven't found a good one...\n            break; \/\/ it must be a bad one. We're done!\n        }\n    }\n    \/\/ Decide whether or not we suspect a missing semicolon\/brace\n    let suspected_missing_semicolon = false;\n    if (semicolon_missing_at !== null && triggering_index >= 0) {\n        \/\/ If the triggering string appears after nothing but whitespace\n        if (\/\\s*\/.test(lines[line].slice(0,triggering_index))) {\n            suspected_missing_semicolon = true;\n        }\n    }\n    \/\/ Select context for error from source lines\n    const context_end = line+1; \/\/ Our context must include the triggering line!\n    const context_start = context_end - 3; \/\/ 3 lines of context by default\n    if (semicolon_missing_at !== null && context_start > semicolon_missing_at) {\n        context_start = semicolon_missing_at; \/\/ Always include the suspected line\n    }\n    if (context_start <= 0) context_start = 0;\n    const context = lines.slice(context_start,context_end);\n    \/\/ Assemble the message\n    if (suspected_missing_semicolon) {\n        const loc_in_context = semicolon_missing_at - context_start;\n        context[loc_in_context] += \" \u25c0\u25c0\u25c0 MISSING SOMETHING?\";\n    }\n    const message = [`When compiling ${name}:\\n`].concat(context);\n    if (triggering_index >= 0) {\n        message.push(' '.repeat(triggering_index) + '\u2580'.repeat(triggering.length));\n    }\n    message.push(error);\n    let longest = 0;\n    for (const l of message) if (l.length > longest) longest = l.length;\n    message.unshift('='.repeat(longest+1));\n    return message.join('\\n');\n}```","title":"Use Our Code #1: Better Errors for Shader Compilation","wayback_source":[]}