blob: 0b54ca9a4fc408dae1168e3a7a0c82d5e87c7f9c [file] [log] [blame]
/*
* Copyright (C) 2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
"use strict";
function generateSPIRV(spirv, program)
{
function findEntryPoints()
{
let entryPoints = [];
for (let functionNames of program.functions.values()) {
for (let func of functionNames) {
switch (func.shaderType) {
case "vertex":
case "fragment":
entryPoints.push(func);
break;
}
}
}
return entryPoints;
}
let currentId = 3;
let currentLocation = 0;
let typeMap = new Map();
let reverseTypeMap = new Map();
let entryPoints = [];
typeMap.set(program.intrinsics.void, currentId++);
typeMap.set(program.intrinsics.uint32, currentId++);
for (let entryPoint of findEntryPoints()) {
let inlinedShader = program.funcInstantiator.getUnique(entryPoint, []);
_inlineFunction(program, inlinedShader, new VisitingSet(entryPoint));
let typeAnalyzer = new SPIRVTypeAnalyzer(program, typeMap, currentId);
inlinedShader.visit(typeAnalyzer);
currentId = typeAnalyzer.currentId;
currentLocation = 0;
let valueAnalyzer = new SPIRVPrimitiveVariableAnalyzer(program, typeMap, currentId, currentLocation);
inlinedShader.returnType.visit(valueAnalyzer);
currentId = valueAnalyzer.currentId;
let outputValues = valueAnalyzer.result;
let inputValues = [];
for (let parameter of inlinedShader.parameters) {
if (parameter.type.type instanceof StructType) {
let valueAnalyzer = new SPIRVPrimitiveVariableAnalyzer(program, typeMap, currentId, currentLocation, parameter.name);
parameter.visit(valueAnalyzer);
currentId = valueAnalyzer.currentId;
currentLocation = valueAnalyzer.currentLocation;
for (let inputValue of valueAnalyzer.result)
inputValues.push(inputValue);
} else if (parameter.type.type instanceof ArrayRefType) {
// FIXME: Implement this.
}
}
entryPoints.push({ id: currentId++, shader: inlinedShader, inputs: inputValues, outputs: outputValues });
}
for (let type of typeMap) {
if (typeof type[1] == "object")
reverseTypeMap.set(type[1].id, type[0]);
else
reverseTypeMap.set(type[1], type[0]);
}
function emitTypes(assembler) {
let emittedTypes = new Set();
function doEmitTypes(type)
{
if (emittedTypes.has(type[0]))
return;
emittedTypes.add(type[0]);
if (typeof type[1] == "object") {
if (type[1].fieldTypes) {
for (let fieldType of type[1].fieldTypes) {
let key = reverseTypeMap.get(fieldType);
let value = typeMap.get(key);
doEmitTypes([key, value]);
}
switch (type[0]) {
case "struct vec2<> { int32 x; int32 y }":
case "struct vec2<> { uint32 x; uint32 y; }":
case "struct vec2<> { float32 x; float32 y; }":
case "struct vec2<> { float64 x; float64 y; }":
case "struct vec3<> { int32 x; int32 y; int32 z; }":
case "struct vec3<> { uint32 x; uint32 y; uint32 z; }":
case "struct vec3<> { float32 x; float32 y; float32 z; }":
case "struct vec3<> { float64 x; float64 y; float64 z; }":
case "struct vec4<> { int32 x; int32 y; int32 z; int32 w; }":
case "struct vec4<> { uint32 x; uint32 y; uint32 z; uint32 w; }":
case "struct vec4<> { float32 x; float32 y; float32 z; float32 w; }":
case "struct vec4<> { float64 x; float64 y; float64 z; float64 w; }":
assembler.append(new spirv.ops.TypeVector(type[1].id, type[1].fieldTypes[0], type[1].fieldTypes.length));
break;
default:
assembler.append(new spirv.ops.TypeStruct(type[1].id, ...type[1].fieldTypes));
break;
}
} else {
if (!type[1].elementType)
throw new Error("Unknown type!");
let elementType = type[1].elementType;
let key = reverseTypeMap.get(elementType);
let value = typeMap.get(key);
doEmitTypes([key, value]);
let id = currentId++;
assembler.append(new spirv.ops.Constant(typeMap.get(program.intrinsics.uint32), id, type[1].numElements));
assembler.append(new spirv.ops.TypeArray(type[1].id, elementType, id));
}
} else {
switch (type[0].name) {
case "void":
assembler.append(new spirv.ops.TypeVoid(type[1]));
break;
case "bool":
assembler.append(new spirv.ops.TypeBool(type[1]));
break;
case "int32":
assembler.append(new spirv.ops.TypeInt(type[1], 32, 1));
break;
case "uint32":
case "uint8":
assembler.append(new spirv.ops.TypeInt(type[1], 32, 0));
break;
case "float32":
assembler.append(new spirv.ops.TypeFloat(type[1], 32));
break;
case "float64":
assembler.append(new spirv.ops.TypeFloat(type[1], 64));
break;
}
}
}
doEmitTypes([program.intrinsics.uint32, typeMap.get(program.intrinsics.uint32)]);
for (let type of typeMap)
doEmitTypes(type)
}
let constants = new Map();
class ConstantFinder extends Visitor {
visitGenericLiteralType(node)
{
let type = node.type;
while (type instanceof TypeRef)
type = type.type;
let values;
switch (type) {
case program.intrinsics.bool:
values = [node.value];
break;
case program.intrinsics.int32:
case program.intrinsics.uint32:
case program.intrinsics.uint8:
values = [node.value]
break;
case program.intrinsics.float: {
let arrayBuffer = new ArrayBuffer(Math.max(Uint32Array.BYTES_PER_ELEMENT, Float32Array.BYTES_PER_ELEMENT));
let floatView = new Float32Array(arrayBuffer);
let uintView = new Uint32Array(arrayBuffer);
floatView[0] = node.value;
values = uintView;
break;
}
case program.intrinsics.double: {
let arrayBuffer = new ArrayBuffer(Math.max(Uint32Array.BYTES_PER_ELEMENT, Float64Array.BYTES_PER_ELEMENT));
let doubleView = new Float64Array(arrayBuffer);
let uintView = new Uint32Array(arrayBuffer);
doubleView[0] = node.value;
values = uintView;
break;
}
default:
throw new Error("Unrecognized literal.");
}
constants.set(node, { id: currentId++, typeId: typeMap.get(type), type: type, values: values });
}
}
for (let entryPoint of entryPoints)
entryPoint.shader.visit(new ConstantFinder());
let assembler = new SPIRVAssembler();
// 1. All OpCapability instructions
assembler.append(new spirv.ops.Capability(spirv.kinds.Capability.Shader));
assembler.append(new spirv.ops.Capability(spirv.kinds.Capability.Float64));
// 2. Optional OpExtension instructions
// 3. Optional OpExtInstImport instructions
// 4. The single required OpMemoryModel instruction
// FIXME: Figure out if we can use the Simple memory model instead of the GLSL memory model.
// The spec says nothing about what the difference between them is. 💯
assembler.append(new spirv.ops.MemoryModel(spirv.kinds.AddressingModel.Logical, spirv.kinds.MemoryModel.GLSL450));
// 5. All entry point declarations
for (let entryPoint of entryPoints) {
let executionModel;
switch (entryPoint.shader.shaderType) {
case "vertex":
executionModel = spirv.kinds.ExecutionModel.Vertex;
break;
case "fragment":
executionModel = spirv.kinds.ExecutionModel.Fragment;
break;
}
let id = entryPoint.id;
let name = entryPoint.shader.name;
let interfaceIds = []
for (let value of entryPoint.inputs)
interfaceIds.push(value.id);
for (let value of entryPoint.outputs)
interfaceIds.push(value.id);
assembler.append(new spirv.ops.EntryPoint(executionModel, id, name, ...interfaceIds));
}
// 6. All execution mode declarations
for (let entryPoint of entryPoints) {
let id = entryPoint.id;
assembler.append(new spirv.ops.ExecutionMode(id, spirv.kinds.ExecutionMode.OriginLowerLeft));
}
// 7. These debug instructions
// 8. All annotation instructions
// FIXME: There are probably more annotations that are required than just location.
let locations = [];
for (let entryPoint of entryPoints) {
switch (entryPoint.shader.shaderType) {
case "vertex":
for (let input of entryPoint.inputs) {
assembler.append(new spirv.ops.Decorate(input.id, spirv.kinds.Decoration.Location, input.location));
locations.push({ name: entryPoint.shader.name + "." + input.name, location: input.location });
}
break;
case "fragment":
for (let output of entryPoint.outputs) {
assembler.append(new spirv.ops.Decorate(output.id, spirv.kinds.Decoration.Location, output.location));
locations.push({ name: entryPoint.shader.name + "." + output.name, location: output.location });
}
break;
}
}
// 9. All type declarations, all constant instructions, and all global variable declarations
emitTypes(assembler);
let functionType = currentId++;
assembler.append(new spirv.ops.TypeFunction(functionType, typeMap.get(program.intrinsics.void)));
for (let constant of constants) {
if (constant[1].type == program.intrinsics.bool) {
if (constant[1].value[0])
assembler.append(new spirv.ops.ConstantTrue(constant[1].id));
else
assembler.append(new spirv.ops.ConstantFalse(constant[1].id));
} else
assembler.append(new spirv.ops.Constant(constant[1].typeId, constant[1].id, ...constant[1].values));
}
for (let entryPoint of entryPoints) {
for (let input of entryPoint.inputs)
assembler.append(new spirv.ops.Variable(input.type, input.id, spirv.kinds.StorageClass.Input));
for (let output of entryPoint.outputs)
assembler.append(new spirv.ops.Variable(output.type, output.id, spirv.kinds.StorageClass.Output));
}
// 10. All function declarations
// 11. All function definitions
for (let entryPoint of entryPoints) {
assembler.append(new spirv.ops.Function(typeMap.get(program.intrinsics.void), entryPoint.id, [spirv.kinds.FunctionControl.None], functionType));
assembler.append(new spirv.ops.FunctionEnd());
}
return { file: assembler.result, locations: locations };
}