| /* |
| * 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 }; |
| } |
| |