blob: 3a7e948be67d0b5c05bb19c2ef14309a119738e4 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<script src="Node.js"></script>
<script src="Type.js"></script>
<script src="ReferenceType.js"></script>
<script src="Value.js"></script>
<script src="Expression.js"></script>
<script src="Rewriter.js"></script>
<script src="Visitor.js"></script>
<script src="CreateLiteral.js"></script>
<script src="CreateLiteralType.js"></script>
<script src="PropertyAccessExpression.js"></script>
<script src="AddressSpace.js"></script>
<script src="AnonymousVariable.js"></script>
<script src="ArrayRefType.js"></script>
<script src="ArrayType.js"></script>
<script src="Assignment.js"></script>
<script src="AutoWrapper.js"></script>
<script src="Block.js"></script>
<script src="BoolLiteral.js"></script>
<script src="Break.js"></script>
<script src="CallExpression.js"></script>
<script src="CallFunction.js"></script>
<script src="Check.js"></script>
<script src="CheckLiteralTypes.js"></script>
<script src="CheckLoops.js"></script>
<script src="CheckRecursion.js"></script>
<script src="CheckRecursiveTypes.js"></script>
<script src="CheckReturns.js"></script>
<script src="CheckUnreachableCode.js"></script>
<script src="CheckWrapped.js"></script>
<script src="Checker.js"></script>
<script src="CloneProgram.js"></script>
<script src="CommaExpression.js"></script>
<script src="ConstexprFolder.js"></script>
<script src="ConstexprTypeParameter.js"></script>
<script src="Continue.js"></script>
<script src="ConvertPtrToArrayRefExpression.js"></script>
<script src="DoWhileLoop.js"></script>
<script src="DotExpression.js"></script>
<script src="DoubleLiteral.js"></script>
<script src="DoubleLiteralType.js"></script>
<script src="DereferenceExpression.js"></script>
<script src="EArrayRef.js"></script>
<script src="EBuffer.js"></script>
<script src="EBufferBuilder.js"></script>
<script src="EPtr.js"></script>
<script src="EnumLiteral.js"></script>
<script src="EnumMember.js"></script>
<script src="EnumType.js"></script>
<script src="EvaluationCommon.js"></script>
<script src="Evaluator.js"></script>
<script src="ExpressionFinder.js"></script>
<script src="ExternalOrigin.js"></script>
<script src="Field.js"></script>
<script src="FindHighZombies.js"></script>
<script src="FlattenProtocolExtends.js"></script>
<script src="FlattenedStructOffsetGatherer.js"></script>
<script src="FloatLiteral.js"></script>
<script src="FloatLiteralType.js"></script>
<script src="FoldConstexprs.js"></script>
<script src="ForLoop.js"></script>
<script src="Func.js"></script>
<script src="FuncDef.js"></script>
<script src="FuncInstantiator.js"></script>
<script src="FuncParameter.js"></script>
<script src="FunctionLikeBlock.js"></script>
<script src="HighZombieFinder.js"></script>
<script src="IdentityExpression.js"></script>
<script src="IfStatement.js"></script>
<script src="IndexExpression.js"></script>
<script src="InferTypesForCall.js"></script>
<script src="Inline.js"></script>
<script src="Inliner.js"></script>
<script src="InstantiateImmediates.js"></script>
<script src="IntLiteral.js"></script>
<script src="IntLiteralType.js"></script>
<script src="Intrinsics.js"></script>
<script src="LateChecker.js"></script>
<script src="Lexer.js"></script>
<script src="LexerToken.js"></script>
<script src="LiteralTypeChecker.js"></script>
<script src="LogicalExpression.js"></script>
<script src="LogicalNot.js"></script>
<script src="LoopChecker.js"></script>
<script src="MakeArrayRefExpression.js"></script>
<script src="MakePtrExpression.js"></script>
<script src="NameContext.js"></script>
<script src="NameFinder.js"></script>
<script src="NameResolver.js"></script>
<script src="NativeFunc.js"></script>
<script src="NativeFuncInstance.js"></script>
<script src="NativeType.js"></script>
<script src="NativeTypeInstance.js"></script>
<script src="NormalUsePropertyResolver.js"></script>
<script src="NullLiteral.js"></script>
<script src="NullType.js"></script>
<script src="OriginKind.js"></script>
<script src="OverloadResolutionFailure.js"></script>
<script src="Parse.js"></script>
<script src="Prepare.js"></script>
<script src="PropertyResolver.js"></script>
<script src="Program.js"></script>
<script src="ProgramWithUnnecessaryThingsRemoved.js"></script>
<script src="Protocol.js"></script>
<script src="ProtocolDecl.js"></script>
<script src="ProtocolFuncDecl.js"></script>
<script src="ProtocolRef.js"></script>
<script src="PtrType.js"></script>
<script src="ReadModifyWriteExpression.js"></script>
<script src="RecursionChecker.js"></script>
<script src="RecursiveTypeChecker.js"></script>
<script src="ResolveNames.js"></script>
<script src="ResolveOverloadImpl.js"></script>
<script src="ResolveProperties.js"></script>
<script src="ResolveTypeDefs.js"></script>
<script src="Return.js"></script>
<script src="ReturnChecker.js"></script>
<script src="ReturnException.js"></script>
<script src="StandardLibrary.js"></script>
<script src="StatementCloner.js"></script>
<script src="StructLayoutBuilder.js"></script>
<script src="StructType.js"></script>
<script src="Substitution.js"></script>
<script src="SwitchCase.js"></script>
<script src="SwitchStatement.js"></script>
<script src="SynthesizeEnumFunctions.js"></script>
<script src="SynthesizeStructAccessors.js"></script>
<script src="TrapStatement.js"></script>
<script src="TypeDef.js"></script>
<script src="TypeDefResolver.js"></script>
<script src="TypeOrVariableRef.js"></script>
<script src="TypeParameterRewriter.js"></script>
<script src="TypeRef.js"></script>
<script src="TypeVariable.js"></script>
<script src="TypeVariableTracker.js"></script>
<script src="TypedValue.js"></script>
<script src="UintLiteral.js"></script>
<script src="UintLiteralType.js"></script>
<script src="UnificationContext.js"></script>
<script src="UnreachableCodeChecker.js"></script>
<script src="VariableDecl.js"></script>
<script src="VariableRef.js"></script>
<script src="VisitingSet.js"></script>
<script src="WSyntaxError.js"></script>
<script src="WTrapError.js"></script>
<script src="WTypeError.js"></script>
<script src="WhileLoop.js"></script>
<script src="WrapChecker.js"></script>
<style>
#ShaderSource {
font-family: monospace;
width: 700px;
height: 400px;
}
td {
border: 1px solid black;
}
</style>
<script>
let defaultShaderSource = `struct VertexInput {
float2 position;
float3 color;
}
struct VertexOutput {
float4 wsl_Position;
float3 color;
}
struct FragmentOutput {
float4 wsl_Color;
}
vertex VertexOutput vertexShader(VertexInput vertexInput) {
VertexOutput result;
result.wsl_Position = float4(vertexInput.position, 0., 1.);
result.color = vertexInput.color;
return result;
}
fragment FragmentOutput fragmentShader(VertexOutput stageIn) {
FragmentOutput result;
result.wsl_Color = float4(stageIn.color, 1.);
return result;
}`;
let shaderSourceTextarea;
let compileButton;
let localStorage;
let compileLogElement;
let vertexShaderSelect;
let fragmentShaderSelect;
let dataTable;
let resultTable;
let program;
let availableVertexShaders;
let availableFragmentShaders;
let currentVertexShader;
let currentFragmentShader;
let inlinedVertexShader;
let inlinedFragmentShader;
let allArgumentInfos;
let argumentEPtrs;
let stageInArgumentIndex;
window.addEventListener("load", function() {
shaderSourceTextarea = document.getElementById("ShaderSource");
compileButton = document.getElementById("CompileButton");
compileButton.addEventListener("click", compilePresentShaderSource);
compileLogElement = document.getElementById("CompileLog");
vertexShaderSelect = document.getElementById("VertexShaderSelect");
fragmentShaderSelect = document.getElementById("FragmentShaderSelect");
vertexShaderSelect.addEventListener("change", selectedShadersChanged);
fragmentShaderSelect.addEventListener("change", selectedShadersChanged);
dataTable = document.getElementById("DataTable");
document.getElementById("RunButton").addEventListener("click", runShaders);
resultTable = document.getElementById("ResultTable");
shaderSourceTextarea.addEventListener("input", sourceCodeChanged);
let shaderSource;
try {
localStorage = window.localStorage;
shaderSource = localStorage.getItem("ShaderSource");
} catch (e) {
}
if (!shaderSource)
shaderSource = defaultShaderSource;
shaderSourceTextarea.value = shaderSource;
window.setTimeout(compilePresentShaderSource, 0);
});
function clearDataTable()
{
while (dataTable.lastChild instanceof HTMLTableRowElement)
dataTable.removeChild(dataTable.lastChild);
}
function clearResultTable()
{
while (resultTable.lastChild instanceof HTMLTableRowElement)
resultTable.removeChild(resultTable.lastChild);
}
function clearShaderSelect()
{
while (vertexShaderSelect.firstChild)
vertexShaderSelect.removeChild(vertexShaderSelect.firstChild);
while (fragmentShaderSelect.firstChild)
fragmentShaderSelect.removeChild(fragmentShaderSelect.firstChild);
}
function sourceCodeChanged() {
compileLogElement.textContent = "";
clearShaderSelect();
clearDataTable();
clearResultTable();
}
function presentError(message) {
compileLogElement.textContent = message;
clearShaderSelect();
clearDataTable();
clearResultTable();
throw new Error(message);
}
function compilePresentShaderSource() {
compileLogElement.textContent = "Compiling...";
compileButton.disabled = true;
window.setTimeout(doCompilePresentShaderSource, 0);
}
function doCompilePresentShaderSource() {
compileButton.disabled = false;
let start = new Date().getTime();
let result;
try {
result = prepare("/internal/test", 0, shaderSourceTextarea.value);
} catch (e) {
result = e;
}
let end = new Date().getTime();
if (!(result instanceof Program)) {
compileLogElement.textContent = result.toString();
return;
} else
compileLogElement.textContent = "Compile successful!";
try {
shaderSource = localStorage.setItem("ShaderSource", shaderSourceTextarea.value);
} catch (e) {
}
program = result;
availableVertexShaders = [];
availableFragmentShaders = [];
for (let functionNames of program.functions.values()) {
for (let func of functionNames) {
if (func.shaderType == "vertex")
availableVertexShaders.push(func);
if (func.shaderType == "fragment")
availableFragmentShaders.push(func);
}
}
clearShaderSelect();
function createOption(textContent) {
let option = document.createElement("option");
option.textContent = textContent;
return option;
}
for (let i = 0; i < availableVertexShaders.length; ++i)
vertexShaderSelect.appendChild(createOption(availableVertexShaders[i].name));
for (let i = 0; i < availableFragmentShaders.length; ++i)
fragmentShaderSelect.appendChild(createOption(availableFragmentShaders[i].name));
selectedShadersChanged(true);
}
function createTableRow(name, elementCallback) {
let row = document.createElement("tr");
let variableCell = document.createElement("td");
let valueCell = document.createElement("td");
variableCell.textContent = name;
valueCell.appendChild(elementCallback());
row.appendChild(variableCell);
row.appendChild(valueCell);
return row;
dataTable.appendChild(row);
}
function linkError(message) {
compileLogElement.textContent = message + " Fix this by selecting a different pair of shaders below.";
clearDataTable();
clearResultTable();
}
function selectedShadersChanged(fromCompilation = false) {
if (!fromCompilation)
compileLogElement.textContent = "";
currentVertexShader = availableVertexShaders[vertexShaderSelect.selectedIndex];
currentFragmentShader = availableFragmentShaders[fragmentShaderSelect.selectedIndex];
inlinedVertexShader = program.funcInstantiator.getUnique(currentVertexShader, []);
_inlineFunction(program, inlinedVertexShader, new VisitingSet(currentVertexShader));
allArgumentInfos = [];
argumentEPtrs = [];
for (let parameter of inlinedVertexShader.parameters) {
let ePtr = new EPtr(new EBuffer(parameter.type.size), 0);
argumentEPtrs.push({ePtr: ePtr, size: parameter.type.size});
let gatherer = new FlattenedStructOffsetGatherer(parameter.name);
parameter.visit(gatherer);
for (let argumentInfo of gatherer.result) {
argumentInfo.ePtr = ePtr;
allArgumentInfos.push(argumentInfo);
}
}
clearDataTable();
clearResultTable();
for (let info of allArgumentInfos) {
dataTable.appendChild(createTableRow(info.name, function() {
let input = document.createElement("input");
input.addEventListener("input", clearResultTable);
input.type = "text";
return input;
}));
}
let found = false;
let index = 0;
for (let parameter of currentFragmentShader.parameters) {
if (parameter.name == "stageIn") {
found = true;
if (!parameter.type.equals(currentVertexShader.returnType))
linkError("Vertex shader's return type needs to match the stageIn parameter of the fragment shader!");
stageInArgumentIndex = index;
break;
}
++index;
}
if (!found)
linkError("Could not find the stageIn argument in the fragment shader!");
if (func.parameters.length > 1)
linkError("Fragment shaders currently don't know how to have any other arguments other than stageIn.");
}
function runShaders() {
let func = inlinedVertexShader;
let index = 0;
for (let i = 0; i < dataTable.children.length; ++i) {
let childElement = dataTable.children[i];
if (!(childElement instanceof HTMLTableRowElement))
continue;
let value = childElement.children[1].firstChild.value;
let argumentInfo = allArgumentInfos[index];
let type = argumentInfo.type;
switch (type) {
case "float32":
value = Math.fround(parseFloat(value));
argumentInfo.ePtr.set(argumentInfo.offset, value);
break;
case "float64":
value = parseFloat(value);
break;
case "int32":
value = parseInt(value) | 0;
break;
case "uint32":
value = parseInt(value) >>> 0;
break;
default:
presentError("I don't know how to parse values of type " + type);
}
if (isNaN(value))
presentError("Could not parse input data " + index);
argumentInfo.ePtr.set(argumentInfo.offset, value);
++index;
}
for (let i = 0; i < func.parameters.length; ++i)
func.parameters[i].ePtr.copyFrom(argumentEPtrs[i].ePtr, argumentEPtrs[i].size);
let result = new Evaluator(program).runFunc(func);
inlinedFragmentShader = program.funcInstantiator.getUnique(currentFragmentShader, []);
_inlineFunction(program, inlinedFragmentShader, new VisitingSet(currentFragmentShader));
func = inlinedFragmentShader;
func.parameters[stageInArgumentIndex].ePtr.copyFrom(result, func.parameters[stageInArgumentIndex].type.size);
result = new Evaluator(program).runFunc(func);
clearResultTable();
let gatherer = new FlattenedStructOffsetGatherer(func.returnType.type.name);
func.returnType.type.visit(gatherer);
for (let info of gatherer.result) {
resultTable.appendChild(createTableRow(info.name, function() {
return document.createTextNode(result.get(info.offset));
}));
}
}
</script>
</head>
<body>
<h1>WebGPU Shading Language</h1>
<ol>
<li>
Write a shader!
<div><textarea id="ShaderSource"></textarea></div>
<div id="CompileLog"></div>
<div><input type="button" value="Compile" id="CompileButton"></div>
</li>
<li>
Select your shaders
<div><label>Vertex Shader <select id="VertexShaderSelect"></select></label></div>
<div><label>Fragment Shader <select id="FragmentShaderSelect"></select></label></div>
</li>
<li>
Specify input data
<table id="DataTable">
<thead>
<tr>
<td>Variable</td>
<td>Value</td>
</tr>
</thead>
</table>
</li>
<li>
<div><input type="button" value="Run" id="RunButton"></div>
<table id="ResultTable">
<thead>
<tr>
<td>Variable</td>
<td>Value</td>
</tr>
</thead>
</table>
</li>
</ol>
</body>
</html>