GLSL to WGSL converter (using TintWASM)

Convert from GLSL to WGSL using the WASM version of the Tint library

by cx20

HTML

<script src="https://cx20.github.io/webgpu-test/libs/glslang.js"></script>
<!-- see: https://forum.babylonjs.com/t/can-i-use-the-tintwasm-library-in-the-babylon-js-repository/23673 -->
<script src="https://cx20.github.io/webgpu-test/libs/twgsl.js"></script>

<script src="https://rawcdn.githack.com/codemirror/CodeMirror/v3.12/lib/codemirror.js"></script>
<script src="https://rawcdn.githack.com/codemirror/CodeMirror/v3.12/mode/clike/clike.js"></script>
<script src="https://rawcdn.githack.com/codemirror/CodeMirror/v3.12/mode/rust/rust.js"></script>

<select name="shader_type" id="shader_type">
<option value="vertex">vertex</option>
<option value="fragment">fragment</option>
</select>

<div><textarea id="glsl" name="glsl">
#version 450
layout(location = 0) in vec3 position;
void main() {
    gl_Position = vec4(position, 1.0);
}
</textarea></div>
<input type="button" id="convert" value="Convert GLSL to WGSL" />
<div><textarea id="wgsl" name="wgsl"></textarea></div>

CSS

/* BASICS */

.CodeMirror {
  /* Set height, width, borders, and global font properties here */
  font-family: monospace;
  height: auto;
}
.CodeMirror-scroll {
  /* Set scrolling behaviour here */
  overflow: auto;
}

/* PADDING */

.CodeMirror-lines {
  padding: 4px 0; /* Vertical padding around content */
}
.CodeMirror pre {
  padding: 0 4px; /* Horizontal padding of content */
}

.CodeMirror-scrollbar-filler {
  background-color: white; /* The little square between H and V scrollbars */
}

/* GUTTER */

.CodeMirror-gutters {
  border-right: 1px solid #ddd;
  background-color: #f7f7f7;
}
.CodeMirror-linenumbers {}
.CodeMirror-linenumber {
  padding: 0 3px 0 5px;
  min-width: 20px;
  text-align: right;
  color: #999;
}

/* CURSOR */

.CodeMirror div.CodeMirror-cursor {
  border-left: 1px solid black;
  z-index: 3;
}
/* Shown when moving in bi-directional text */
.CodeMirror div.CodeMirror-secondarycursor {
  border-left: 1px solid silver;
}
.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
  width: auto;
  border: 0;
  background: #7e7;
  z-index: 1;
}
/* Can style cursor different in overwrite (non-insert) mode */
.CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}

.cm-tab { display: inline-block; }

/* DEFAULT THEME */

.cm-s-default .cm-keyword {color: #708;}
.cm-s-default .cm-atom {color: #219;}
.cm-s-default .cm-number {color: #164;}
.cm-s-default .cm-def {color: #00f;}
.cm-s-default .cm-variable {color: black;}
.cm-s-default .cm-variable-2 {color: #05a;}
.cm-s-default .cm-variable-3 {color: #085;}
.cm-s-default .cm-property {color: black;}
.cm-s-default .cm-operator {color: black;}
.cm-s-default .cm-comment {color: #a50;}
.cm-s-default .cm-string {color: #a11;}
.cm-s-default .cm-string-2 {color: #f50;}
.cm-s-default .cm-meta {color: #555;}
.cm-s-default .cm-error {color: #f00;}
.cm-s-default .cm-qualifier {color: #555;}
.cm-s-default .cm-builtin {color: #30a;}
.cm-s-default .cm-bracket {color: #997;}
.cm-s-default .cm-tag {color:...

JavaScript

const VERTEX_SHADER_SAMPLE = `#version 450
layout(location = 0) in vec3 position;
void main() {
    gl_Position = vec4(position, 1.0);
}
`;
const FRAGMENT_SHADER_SAMPLE = `#version 450
layout(location = 0) out vec4 outColor;
void main() {
    outColor = vec4(0.0, 0.0, 1.0, 1.0);
}
`;

let libGlslang = null;
let libTwgsl = null;

let promise1 = glslang();
let promise2 = twgsl("https://cx20.github.io/webgpu-test/libs/twgsl.wasm");

Promise.all([promise1, promise2]).then((args) => {
    libGlslang = args[0];
    libTwgsl = args[1];
});

let editorGLSL = CodeMirror.fromTextArea(document.getElementById("glsl"), {
    lineNumbers: true,
    matchBrackets: true,
    autofocus: true,
    mode: "text/x-csrc"
});

let editorWGSL = CodeMirror.fromTextArea(document.getElementById("wgsl"), {
    lineNumbers: true,
    matchBrackets: true,
    autofocus: true,
    mode: "text/x-rustsrc"
});

let selectBox = document.getElementById('shader_type');
let convertButton = document.getElementById('convert');

selectBox.addEventListener('change', changeShaderType);
convertButton.addEventListener('click', convertGlslToWgsl);

function changeShaderType() {
    switch (selectBox.value) {
        case "vertex":
            editorGLSL.doc.setValue(VERTEX_SHADER_SAMPLE);
            break;
        case "fragment":
            editorGLSL.doc.setValue(FRAGMENT_SHADER_SAMPLE);
            break;
    }
    editorWGSL.doc.setValue("");
}

function convertGlslToWgsl() {
    let glslCode = editorGLSL.doc.getValue();
    let type = selectBox.value;
    let spirv = libGlslang.compileGLSL(glslCode, type);
    let wgslCode = libTwgsl.convertSpirV2WGSL(spirv);
    editorWGSL.doc.setValue(wgslCode);
}