From e7ddebd2cb3c41db3cfef5e6eb68a40d548adfa9 Mon Sep 17 00:00:00 2001 From: Christopher Crawford Date: Wed, 2 Mar 2022 14:06:08 -0500 Subject: [PATCH] SPIR-V Transpiler Control Flow (flutter/engine#31451) --- engine/src/flutter/lib/spirv/README.md | 8 +- engine/src/flutter/lib/spirv/lib/spirv.dart | 8 +- .../flutter/lib/spirv/lib/src/constants.dart | 68 ++- .../flutter/lib/spirv/lib/src/function.dart | 91 +++- .../lib/spirv/lib/src/instructions.dart | 424 ++++++++++++++++-- .../flutter/lib/spirv/lib/src/transpiler.dart | 173 +++++-- .../lib/spirv/test/exception_shaders/BUILD.gn | 5 + ...loop_does_not_branch_to_conditional.spvasm | 74 +++ .../for_loop_does_not_branch_to_merge.spvasm | 74 +++ ...atements_in_for_loop_continue_block.spvasm | 76 ++++ ...le_variables_in_for_loop_comparison.spvasm | 78 ++++ .../no_store_in_for_loop_continue.spvasm | 66 +++ .../lib/spirv/test/exception_shaders/src.glsl | 13 + .../164_OpLogicalEqual.glsl | 20 + .../165_OpLogicalNotEqual.glsl | 21 + .../supported_op_shaders/166_OpLogicalOr.glsl | 20 + .../167_OpLogicalAnd.glsl | 20 + .../168_OpLogicalNot.glsl | 20 + .../supported_op_shaders/180_OpFOrdEqual.glsl | 20 + .../183_OpFUnordNotEqual.glsl | 20 + .../184_OpFOrdLessThan.glsl | 20 + .../186_OpFOrdGreaterThan.glsl | 20 + .../188_OpFOrdLessThanEqual.glsl | 20 + .../190_OpFOrdGreaterThanEqual.glsl | 20 + .../supported_op_shaders/246_OpLoopMerge.glsl | 28 ++ .../250_OpBranchConditional.glsl | 24 + .../spirv/test/supported_op_shaders/BUILD.gn | 13 + 27 files changed, 1340 insertions(+), 104 deletions(-) create mode 100644 engine/src/flutter/lib/spirv/test/exception_shaders/for_loop_does_not_branch_to_conditional.spvasm create mode 100644 engine/src/flutter/lib/spirv/test/exception_shaders/for_loop_does_not_branch_to_merge.spvasm create mode 100644 engine/src/flutter/lib/spirv/test/exception_shaders/multiple_statements_in_for_loop_continue_block.spvasm create mode 100644 engine/src/flutter/lib/spirv/test/exception_shaders/multiple_variables_in_for_loop_comparison.spvasm create mode 100644 engine/src/flutter/lib/spirv/test/exception_shaders/no_store_in_for_loop_continue.spvasm create mode 100644 engine/src/flutter/lib/spirv/test/exception_shaders/src.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/164_OpLogicalEqual.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/165_OpLogicalNotEqual.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/166_OpLogicalOr.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/167_OpLogicalAnd.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/168_OpLogicalNot.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/180_OpFOrdEqual.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/183_OpFUnordNotEqual.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/184_OpFOrdLessThan.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/186_OpFOrdGreaterThan.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/188_OpFOrdLessThanEqual.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/190_OpFOrdGreaterThanEqual.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/246_OpLoopMerge.glsl create mode 100644 engine/src/flutter/lib/spirv/test/supported_op_shaders/250_OpBranchConditional.glsl diff --git a/engine/src/flutter/lib/spirv/README.md b/engine/src/flutter/lib/spirv/README.md index 6613b972b10..474d87ce68c 100644 --- a/engine/src/flutter/lib/spirv/README.md +++ b/engine/src/flutter/lib/spirv/README.md @@ -19,9 +19,13 @@ the code will need to adhere to the following rules. - The output can only be written to from the main function. - `gl_FragCoord` can only be read from the main function, and its z and w components have no meaning. -- Control flow is prohibited (`if`, `while`, `for`, `switch`, etc.) aside from function calls and `return`. +- `if`, `else`, and `for` and function calls are the only permitted control + flow operations. `for` loops must initialize a float variable to a constant + value, compare it against a constant value, and increment/modify it by a + constant value. +- `while` and `switch` statements are not supported. - No inputs from other shader stages. -- Only sampler2D, float, float-vector types, and square float-matrix types. +- Only sampler2D, bool, float, float-vector types, and square float-matrix types. - Only square matrices are supported. - Only built-in functions present in GLSL ES 100 are used. - Only the `texture` function is supported for sampling from a sampler2D object. diff --git a/engine/src/flutter/lib/spirv/lib/spirv.dart b/engine/src/flutter/lib/spirv/lib/spirv.dart index 6d701fe4ed6..56b0b67921e 100644 --- a/engine/src/flutter/lib/spirv/lib/spirv.dart +++ b/engine/src/flutter/lib/spirv/lib/spirv.dart @@ -44,10 +44,10 @@ class TranspileResult { final int samplerCount; TranspileResult._( - this.src, - this.uniformFloatCount, - this.samplerCount, - this.language, + this.src, + this.uniformFloatCount, + this.samplerCount, + this.language, ); } diff --git a/engine/src/flutter/lib/spirv/lib/src/constants.dart b/engine/src/flutter/lib/spirv/lib/src/constants.dart index a4c5a52f28e..a5549822005 100644 --- a/engine/src/flutter/lib/spirv/lib/src/constants.dart +++ b/engine/src/flutter/lib/spirv/lib/src/constants.dart @@ -70,7 +70,6 @@ const int _opFunction = 54; const int _opFunctionParameter = 55; const int _opFunctionEnd = 56; const int _opFunctionCall = 57; -const int _opFUnordNotEqual = 183; const int _opVariable = 59; const int _opLoad = 61; const int _opStore = 62; @@ -95,8 +94,23 @@ const int _opVectorTimesMatrix = 144; const int _opMatrixTimesVector = 145; const int _opMatrixTimesMatrix = 146; const int _opDot = 148; +const int _opFOrdEqual = 180; +const int _opFUnordNotEqual = 183; +const int _opFOrdLessThan = 184; +const int _opFOrdGreaterThan = 186; +const int _opFOrdLessThanEqual = 188; +const int _opFOrdGreaterThanEqual = 190; +const int _opLogicalEqual = 164; +const int _opLogicalNotEqual = 165; +const int _opLogicalOr = 166; +const int _opLogicalAnd = 167; +const int _opLogicalNot = 168; const int _opSelect = 169; +const int _opLoopMerge = 246; +const int _opSelectionMerge = 247; const int _opLabel = 248; +const int _opBranch = 249; +const int _opBranchConditional = 250; const int _opReturn = 253; const int _opReturnValue = 254; @@ -212,3 +226,55 @@ const Map _glslStd450OpArgc = { _glslStd450FaceForward: 3, _glslStd450Reflect: 2, }; + +enum _Operator { + addition, + subtraction, + division, + multiplication, + modulo, + negation, + equality, + inequality, + and, + or, + not, + lessThan, + greaterThan, + lessThanEqual, + greaterThanEqual, +} + +const Set<_Operator> _compoundAssignmentOperators = <_Operator>{ + _Operator.addition, + _Operator.subtraction, + _Operator.division, + _Operator.multiplication, + _Operator.modulo, +}; + +const Map<_Operator, String> _operatorStrings = <_Operator, String>{ + _Operator.addition: '+', + _Operator.subtraction: '-', + _Operator.division: '/', + _Operator.multiplication: '*', + _Operator.modulo: '%', + _Operator.negation: '-', + _Operator.equality: '==', + _Operator.inequality: '!=', + _Operator.and: '&&', + _Operator.or: '||', + _Operator.not: '!', + _Operator.lessThan: '<', + _Operator.greaterThan: '>', + _Operator.lessThanEqual: '<=', + _Operator.greaterThanEqual: '>=', +}; + +String _operatorString(_Operator op) { + return _operatorStrings[op]!; +} + +bool _isCompoundAssignment(_Operator op) { + return _compoundAssignmentOperators.contains(op); +} diff --git a/engine/src/flutter/lib/spirv/lib/src/function.dart b/engine/src/flutter/lib/spirv/lib/src/function.dart index 45c10943534..50c0fbadd87 100644 --- a/engine/src/flutter/lib/spirv/lib/src/function.dart +++ b/engine/src/flutter/lib/spirv/lib/src/function.dart @@ -4,14 +4,25 @@ part of spirv; +class _Variable { + _Variable(this.id, this.type); + + final int id; + final int type; + + bool initialized = false; + int liftToBlock = 0; +} + class _Function { + _Function(this.transpiler, this.type, this.name) + : params = List.filled(type.params.length, 0); + + final _Transpiler transpiler; final int name; final _FunctionType type; final List params; - _Function(this.type, this.name) : - params = List.filled(type.params.length, 0); - // entry point for the function _Block? entry; @@ -20,46 +31,76 @@ class _Function { final Map blocks = {}; final List deps = []; + final Map variables = {}; _Block addBlock(int id) { - final _Block b = _Block(); + final _Block b = _Block(id, this); blocks[id] = b; entry ??= b; return b; } + _Block block(int id) { + return blocks[id]!; + } + + void declareVariable(int id, int type) { + variables[id] = _Variable(id, type); + } + + _Variable? variable(int id) { + return variables[id]; + } + void declareParam(int id, int paramType) { final int i = declaredParams; if (paramType != type.params[i]) { - throw TranspileException._(_opFunctionParameter, - 'type mismatch for param $i of function $name'); + throw TranspileException._( + _opFunctionParameter, 'type mismatch for param $i of function $name'); } params[i] = id; declaredParams++; } - void write(_Transpiler t, StringBuffer out) { + /// Returns deps of result `id` that are variables. + List<_Variable> variableDeps(int id) { + final _Instruction? result = transpiler.results[id]; + if (result == null) { + return <_Variable>[]; + } + final Set deps = {}; + transpiler.collectDeps(deps, id); + return deps + .where(variables.containsKey) + .map((int id) => variables[id]!) + .toList(); + } + + void write(StringBuffer out) { if (declaredParams != params.length) { - throw t.failure('not all parameters declared for function $name'); + throw transpiler + .failure('not all parameters declared for function $name'); } if (entry == null) { - throw t.failure('function $name has no entry block'); + throw transpiler.failure('function $name has no entry block'); } - String returnTypeString = t.resolveType(type.returnType); - if (t.target == TargetLanguage.sksl && name == t.entryPoint) { + String returnTypeString = transpiler.resolveType(type.returnType); + if (transpiler.target == TargetLanguage.sksl && + name == transpiler.entryPoint) { returnTypeString = 'half4'; } - final String nameString = t.resolveName(name); + final String nameString = transpiler.resolveName(name); out.write('$returnTypeString $nameString('); - if (t.target == TargetLanguage.sksl && name == t.entryPoint) { + if (transpiler.target == TargetLanguage.sksl && + name == transpiler.entryPoint) { const String fragParam = 'float2 $_fragParamName'; out.write(fragParam); } for (int i = 0; i < params.length; i++) { - final String typeString = t.resolveType(type.params[i]); - final String nameString = t.resolveName(params[i]); + final String typeString = transpiler.resolveType(type.params[i]); + final String nameString = transpiler.resolveName(params[i]); out.write('$typeString $nameString'); if (i < params.length - 1) { out.write(', '); @@ -70,22 +111,28 @@ class _Function { // SkSL needs to return a value from main, so we maintain a variable // that receives the value of gl_FragColor and returns it at the end. - if (t.target == TargetLanguage.sksl && name == t.entryPoint) { - if (t.fragCoord > 0) { - final String fragName = t.resolveName(t.fragCoord); + if (transpiler.target == TargetLanguage.sksl && + name == transpiler.entryPoint) { + if (transpiler.fragCoord > 0) { + final String fragName = transpiler.resolveName(transpiler.fragCoord); out.writeln(' float4 $fragName = float4($_fragParamName, 0, 0);'); } out.writeln(' float4 $_colorVariableName;'); } - // write the actual function body - entry?.write(t, out, 1); + entry?._preprocess(); - if (t.target == TargetLanguage.sksl && name == t.entryPoint) { + // write the actual function body + entry?.write(_BlockContext( + out: out, + indent: 1, + )); + + if (transpiler.target == TargetLanguage.sksl && + name == transpiler.entryPoint) { out.writeln(' return $_colorVariableName;'); } out.writeln('}'); out.writeln(); } } - diff --git a/engine/src/flutter/lib/spirv/lib/src/instructions.dart b/engine/src/flutter/lib/spirv/lib/src/instructions.dart index 17fc681feac..13cf7be658f 100644 --- a/engine/src/flutter/lib/spirv/lib/src/instructions.dart +++ b/engine/src/flutter/lib/spirv/lib/src/instructions.dart @@ -1,33 +1,286 @@ part of spirv; +class _BlockContext { + _BlockContext({ + required this.out, + required this.indent, + this.merge = 0, + this.continueBlock = 0, + this.loopHeader = 0, + this.loopMerge = 0, + }); + + final StringBuffer out; + final int indent; + + /// The most local merge block id, or zero. + final int merge; + + /// The most local continue block id, or zero. + final int continueBlock; + + /// The most local loop-construct header block id. + final int loopHeader; + + /// The most local loop-construct merge block id. + /// This is different from [merge] when the context is inside an if-statement + /// inside of a for-loop, for example. + final int loopMerge; + + /// Return a new [_BlockContext] that is a copy of the current [_BlockContext] + /// with an increased indent and any parameters specified here overwritten. + _BlockContext child({ + int? merge, + int? continueBlock, + int? loopHeader, + int? loopMerge, + }) => + _BlockContext( + out: out, + indent: indent + 1, + merge: merge ?? this.merge, + continueBlock: continueBlock ?? this.continueBlock, + loopHeader: loopHeader ?? this.loopHeader, + loopMerge: loopMerge ?? this.loopMerge, + ); + + void writeIndent() { + out.write(' ' * indent); + } +} + class _Block { + _Block(this.id, this.function); + + final int id; + final _Function function; + List<_Instruction> instructions = <_Instruction>[]; - void add(_Instruction i) { + // control flow + int branch = 0; + int mergeBlock = 0; + int condition = 0; + int truthyBlock = 0; + int falseyBlock = 0; + + // structured loop + _Store? loopInitializer; + int continueBlock = 0; + + // true if this block has been processed by [liftLoopVariables]. + bool scanned = false; + + _Transpiler get transpiler => function.transpiler; + + bool get hasSelectionStructure => mergeBlock != 0 && continueBlock == 0; + bool get hasLoopStructure => continueBlock != 0; + + void _add(_Instruction i) { instructions.add(i); } - void writeIndent(StringBuffer out, int indent) { - for (int i = 0; i < indent; i++) { - out.write(' '); + void _writeContinue(_BlockContext ctx) { + final List<_CompoundAssignment> assignments = + instructions.whereType<_CompoundAssignment>().toList(); + if (assignments.isEmpty) { + throw TranspileException._( + _opLoopMerge, 'loop continue block $id has no compound asignments.'); + } + if (assignments.length > 1) { + throw TranspileException._(_opLoopMerge, + 'loop continue block $id has multiple compound assignments.'); + } + assignments[0].write(transpiler, ctx.out); + } + + void write(_BlockContext ctx) { + for (final _Instruction inst in instructions) { + if (inst is _Store) { + final _Variable? v = function.variables[inst.pointer]; + if (v != null && inst.shouldDeclare && v.liftToBlock != 0) { + function.block(v.liftToBlock).loopInitializer = inst; + continue; + } + } + + if (!inst.isResult) { + ctx.writeIndent(); + inst.write(transpiler, ctx.out); + ctx.out.writeln(';'); + } else if (inst.refCount > 1) { + ctx.writeIndent(); + final String typeString = transpiler.resolveType(inst.type); + final String nameString = transpiler.resolveName(inst.id); + ctx.out.write('$typeString $nameString = '); + inst.write(transpiler, ctx.out); + ctx.out.writeln(';'); + } + } + + if (hasSelectionStructure) { + _writeSelectionStructure(ctx); + } else if (hasLoopStructure) { + _writeLoopStructure(ctx); + } + + if (mergeBlock != 0) { + function.block(mergeBlock).write(ctx); + } else if (branch != 0) { + if (branch == ctx.merge) { + return; + } + if (branch == ctx.continueBlock) { + if (ctx.merge != ctx.loopMerge) { + ctx.writeIndent(); + ctx.out.writeln('continue;'); + } + return; + } + if (branch == ctx.loopMerge) { + ctx.writeIndent(); + ctx.out.writeln('break;'); + } + function.block(branch).write(ctx); } } - void write(_Transpiler t, StringBuffer out, int indent) { - for (final _Instruction inst in instructions) { - if (!inst.isResult) { - writeIndent(out, indent); - inst.write(t, out); - out.writeln(';'); - } else if (inst.refCount > 1) { - writeIndent(out, indent); - final String typeString = t.resolveType(inst.type); - final String nameString = t.resolveName(inst.id); - out.write('$typeString $nameString = '); - inst.write(t, out); - out.writeln(';'); + /// Scans through the entire Control-Flow graph to collecting parts of + /// for-loop structures. + void _preprocess() { + if (scanned) { + return; + } + scanned = true; + + // SkSL has specific needs for for-loops - they must define a single + // index variable to a constant value, they must compare that value + // against a constant value, and they must be modified in place with + // a constant value. SPIR-V represents all these operations in different + // blocks, so we scan here to collect them so they can be written together. + if (hasLoopStructure) { + int conditionId = condition; + if (condition == 0) { + final _Block branchBlock = function.block(branch); + if (!branchBlock._isSimple() || branchBlock.condition == 0) { + throw TranspileException._( + _opBranch, + 'block $id has a loop structure but does not immediately ' + 'branch to a single-expression conditional block.'); + } + conditionId = branchBlock.condition; + } + final List<_Variable> deps = function.variableDeps(conditionId); + if (deps.length != 1) { + throw TranspileException._( + _opLoopMerge, + 'block $id has a loop structure with a condition ' + 'using more or fewer than one local variable.'); + } + deps[0].liftToBlock = id; + } + + // Scan all blocks that can be reached from this block. + if (branch != 0) { + function.block(branch)._preprocess(); + } + if (condition != 0) { + if (truthyBlock != 0) { + function.block(truthyBlock)._preprocess(); + } + if (falseyBlock != 0) { + function.block(falseyBlock)._preprocess(); } } + if (mergeBlock != 0) { + function.block(mergeBlock)._preprocess(); + } + } + + void _writeSelectionStructure(_BlockContext ctx) { + final _BlockContext childCtx = ctx.child(merge: mergeBlock); + ctx.writeIndent(); + final String conditionString = transpiler.resolveResult(condition); + ctx.out.writeln('if ($conditionString) {'); + function.block(truthyBlock).write(childCtx); + if (falseyBlock != 0 && falseyBlock != mergeBlock) { + ctx.writeIndent(); + ctx.out.writeln('} else {'); + function.block(falseyBlock).write(childCtx); + } + ctx.writeIndent(); + ctx.out.writeln('}'); + } + + void _writeLoopStructure(_BlockContext ctx) { + final _BlockContext childCtx = ctx.child( + merge: mergeBlock, + continueBlock: continueBlock, + loopHeader: id, + loopMerge: mergeBlock, + ); + + String conditionString; + int loopBody = 0; + if (condition != 0) { + conditionString = transpiler.resolveResult(condition); + if (truthyBlock == mergeBlock) { + conditionString = '!' + conditionString; + loopBody = falseyBlock; + } else if (falseyBlock == mergeBlock) { + loopBody = truthyBlock; + } + } else { + final _Block branchBlock = function.block(branch); + if (!branchBlock._isSimple() || branchBlock.condition == 0) { + throw TranspileException._( + _opBranch, + 'block $id has a loop structure but does not immediately ' + 'branch to a single-expression conditional block.'); + } + + conditionString = transpiler.resolveResult(branchBlock.condition); + if (branchBlock.truthyBlock == mergeBlock) { + conditionString = '!' + conditionString; + loopBody = branchBlock.falseyBlock; + } else if (branchBlock.falseyBlock == mergeBlock) { + loopBody = branchBlock.truthyBlock; + } + } + + if (loopBody == 0) { + throw TranspileException._( + _opLoopMerge, + 'block $id does not conditionally branch to its ' + 'loop merge block.'); + } + + ctx.writeIndent(); + ctx.out.write('for('); + loopInitializer!.write(transpiler, ctx.out); + ctx.out.write('; '); + ctx.out.write(conditionString); + ctx.out.write('; '); + function.block(continueBlock)._writeContinue(ctx); + ctx.out.writeln(') {'); + function.block(loopBody).write(childCtx); + ctx.writeIndent(); + ctx.out.writeln('}'); + } + + /// Returns true if this block has no stateful expressions + /// and can be written as a single expression. + bool _isSimple() { + int statements = 0; + for (final _Instruction inst in instructions) { + if (!inst.isResult) { + return false; + } + if (inst.refCount > 1) { + statements++; + } + } + return statements == 0; } } @@ -38,6 +291,8 @@ abstract class _Instruction { bool get isResult => id != 0; + List get deps => []; + // How many times this instruction is referenced, a value // of 2 or greater means that it will be stored into a variable. int refCount = 0; @@ -46,6 +301,8 @@ abstract class _Instruction { } class _FunctionCall extends _Instruction { + _FunctionCall(this.type, this.id, this.function, this.args); + @override final int type; @@ -55,7 +312,8 @@ class _FunctionCall extends _Instruction { final String function; final List args; - _FunctionCall(this.type, this.id, this.function, this.args); + @override + List get deps => args; @override void write(_Transpiler t, StringBuffer out) { @@ -70,18 +328,16 @@ class _FunctionCall extends _Instruction { } } -class _StringInstruction extends _Instruction { - final String value; - - _StringInstruction(this.value); - +class _Return extends _Instruction { @override void write(_Transpiler t, StringBuffer out) { - out.write(value); + out.write('return'); } } class _Select extends _Instruction { + _Select(this.type, this.id, this.condition, this.a, this.b); + @override final int type; @@ -92,32 +348,74 @@ class _Select extends _Instruction { final int a; final int b; - _Select(this.type, this.id, this.condition, this.a, this.b); + @override + List get deps => [condition, a, b]; @override void write(_Transpiler t, StringBuffer out) { - final String typeName = t.resolveType(type); final String aName = t.resolveResult(a); final String bName = t.resolveResult(b); final String conditionName = t.resolveResult(condition); - out.write('mix($bName, $aName, $typeName($conditionName))'); + out.write('$conditionName ? $aName : $bName'); } } -class _Store extends _Instruction { +class _CompoundAssignment extends _Instruction { + _CompoundAssignment(this.pointer, this.op, this.object); + final int pointer; + final _Operator op; final int object; - _Store(this.pointer, this.object); @override void write(_Transpiler t, StringBuffer out) { final String pointerName = t.resolveResult(pointer); final String objectName = t.resolveResult(object); - out.write('$pointerName = $objectName'); + final String operatorString = _operatorString(op); + out.write('$pointerName $operatorString= $objectName'); + } +} + +class _Store extends _Instruction { + _Store( + this.pointer, + this.object, { + this.shouldDeclare = false, + this.declarationType = 0, + }); + + final int pointer; + final int object; + + final bool shouldDeclare; + final int declarationType; + + int selfModifyObject = 0; + String selfModifyOperator = ''; + + @override + List get deps => [pointer, object]; + + @override + void write(_Transpiler t, StringBuffer out) { + final String pointerName = t.resolveResult(pointer); + if (selfModifyObject > 0) { + final String objectName = t.resolveResult(selfModifyObject); + out.write('$pointerName $selfModifyOperator $objectName'); + } else { + final String objectName = t.resolveResult(object); + if (shouldDeclare) { + final String typeString = t.resolveType(declarationType); + out.write('$typeString '); + } + out.write('$pointerName = $objectName'); + } } } class _AccessChain extends _Instruction { + _AccessChain(this.type, this.id, this.base, this.indices); + @override final int type; @@ -127,7 +425,8 @@ class _AccessChain extends _Instruction { final int base; final List indices; - _AccessChain(this.type, this.id, this.base, this.indices); + @override + List get deps => [base, ...indices]; @override void write(_Transpiler t, StringBuffer out) { @@ -140,6 +439,8 @@ class _AccessChain extends _Instruction { } class _VectorShuffle extends _Instruction { + _VectorShuffle(this.type, this.id, this.vector, this.indices); + @override final int type; @@ -149,7 +450,8 @@ class _VectorShuffle extends _Instruction { final int vector; final List indices; - _VectorShuffle(this.type, this.id, this.vector, this.indices); + @override + List get deps => [vector]; @override void write(_Transpiler t, StringBuffer out) { @@ -168,6 +470,8 @@ class _VectorShuffle extends _Instruction { } class _CompositeConstruct extends _Instruction { + _CompositeConstruct(this.type, this.id, this.components); + @override final int type; @@ -176,7 +480,8 @@ class _CompositeConstruct extends _Instruction { final List components; - _CompositeConstruct(this.type, this.id, this.components); + @override + List get deps => components; @override void write(_Transpiler t, StringBuffer out) { @@ -193,6 +498,8 @@ class _CompositeConstruct extends _Instruction { } class _CompositeExtract extends _Instruction { + _CompositeExtract(this.type, this.id, this.src, this.indices); + @override final int type; @@ -202,7 +509,8 @@ class _CompositeExtract extends _Instruction { final int src; final List indices; - _CompositeExtract(this.type, this.id, this.src, this.indices); + @override + List get deps => [src]; @override void write(_Transpiler t, StringBuffer out) { @@ -214,6 +522,9 @@ class _CompositeExtract extends _Instruction { } class _ImageSampleImplicitLod extends _Instruction { + _ImageSampleImplicitLod( + this.type, this.id, this.sampledImage, this.coordinate); + @override final int type; @@ -223,42 +534,51 @@ class _ImageSampleImplicitLod extends _Instruction { final int sampledImage; final int coordinate; - _ImageSampleImplicitLod(this.type, this.id, this.sampledImage, this.coordinate); + @override + List get deps => [coordinate]; @override void write(_Transpiler t, StringBuffer out) { final String sampledImageString = t.resolveName(sampledImage); final String coordinateString = t.resolveResult(coordinate); if (t.target == TargetLanguage.sksl) { - out.write('$sampledImageString.eval(${sampledImageString}_size * $coordinateString)'); + out.write( + '$sampledImageString.eval(${sampledImageString}_size * $coordinateString)'); } else { out.write('texture($sampledImageString, $coordinateString)'); } } } -class _Negate extends _Instruction { +class _UnaryOperator extends _Instruction { + _UnaryOperator(this.type, this.id, this.op, this.operand); + @override final int type; @override final int id; + final _Operator op; final int operand; - _Negate(this.type, this.id, this.operand); + @override + List get deps => [operand]; @override void write(_Transpiler t, StringBuffer out) { - final String operandString = t.resolveResult(operand); - out.write('-$operandString'); + out.write(_operatorString(op)); + out.write(t.resolveResult(operand)); } } class _ReturnValue extends _Instruction { + _ReturnValue(this.value); + final int value; - _ReturnValue(this.value); + @override + List get deps => [value]; @override void write(_Transpiler t, StringBuffer out) { @@ -267,28 +587,34 @@ class _ReturnValue extends _Instruction { } } -class _Operator extends _Instruction { +class _BinaryOperator extends _Instruction { + _BinaryOperator(this.type, this.id, this.op, this.a, this.b); + @override final int type; @override final int id; - final String op; + final _Operator op; final int a; final int b; - _Operator(this.type, this.id, this.op, this.a, this.b); + @override + List get deps => [a, b]; @override void write(_Transpiler t, StringBuffer out) { final String aStr = t.resolveResult(a); final String bStr = t.resolveResult(b); - out.write('$aStr $op $bStr'); + final String opString = _operatorString(op); + out.write('$aStr $opString $bStr'); } } class _BuiltinFunction extends _Instruction { + _BuiltinFunction(this.type, this.id, this.function, this.args); + @override final int type; @@ -298,7 +624,8 @@ class _BuiltinFunction extends _Instruction { final String function; final List args; - _BuiltinFunction(this.type, this.id, this.function, this.args); + @override + List get deps => args; @override void write(_Transpiler t, StringBuffer out) { @@ -314,6 +641,8 @@ class _BuiltinFunction extends _Instruction { } class _TypeCast extends _Instruction { + _TypeCast(this.type, this.id, this.value); + @override final int type; @@ -322,7 +651,8 @@ class _TypeCast extends _Instruction { final int value; - _TypeCast(this.type, this.id, this.value); + @override + List get deps => [value]; @override void write(_Transpiler t, StringBuffer out) { diff --git a/engine/src/flutter/lib/spirv/lib/src/transpiler.dart b/engine/src/flutter/lib/spirv/lib/src/transpiler.dart index 7e78d1d574e..7259727a31f 100644 --- a/engine/src/flutter/lib/spirv/lib/src/transpiler.dart +++ b/engine/src/flutter/lib/spirv/lib/src/transpiler.dart @@ -201,6 +201,23 @@ class _Transpiler { TranspileException failure(String why) => TranspileException._(currentOp, why); + void collectDeps(Set collectedDeps, int id) { + if (alias.containsKey(id)) { + id = alias[id]!; + collectedDeps.add(id); + } + final _Instruction? result = results[id]; + if (result == null) { + return; + } + for (final int i in result.deps) { + if (!collectedDeps.contains(i)) { + collectedDeps.add(i); + collectDeps(collectedDeps, i); + } + } + } + void writeFunctionAndDeps(Set visited, int function) { if (visited.contains(function)) { return; @@ -210,7 +227,7 @@ class _Transpiler { for (final int dep in f.deps) { writeFunctionAndDeps(visited, dep); } - f.write(this, src); + f.write(src); } void writeHeader() { @@ -229,6 +246,13 @@ class _Transpiler { } } + int resolveId(int id) { + if (alias.containsKey(id)) { + return alias[id]!; + } + return id; + } + String resolveName(int id) { if (alias.containsKey(id)) { return resolveName(alias[id]!); @@ -239,7 +263,8 @@ class _Transpiler { return 'true'; } else if (constantFalse > 0 && id == constantFalse) { return 'false'; - } if (id == colorOutput) { + } + if (id == colorOutput) { if (target == TargetLanguage.glslES) { return _glslESColorName; } else { @@ -323,7 +348,7 @@ class _Transpiler { if (inst.isResult) { results[inst.id] = inst; } - currentBlock!.add(inst); + currentBlock!._add(inst); } /// Read an instruction word, and handle the operation. @@ -446,39 +471,81 @@ class _Transpiler { opImageSampleImplicitLod(); break; case _opFNegate: - opFNegate(); + parseUnaryOperator(_Operator.subtraction); break; case _opFAdd: - parseOperatorInst('+'); + parseOperatorInst(_Operator.addition); break; case _opFSub: - parseOperatorInst('-'); + parseOperatorInst(_Operator.negation); break; case _opFMul: - parseOperatorInst('*'); + parseOperatorInst(_Operator.multiplication); break; case _opFDiv: - parseOperatorInst('/'); + parseOperatorInst(_Operator.division); break; case _opFMod: parseBuiltinFunction('mod'); break; - case _opFUnordNotEqual: - parseOperatorInst('!='); - break; case _opVectorTimesScalar: case _opMatrixTimesScalar: case _opVectorTimesMatrix: case _opMatrixTimesVector: case _opMatrixTimesMatrix: - parseOperatorInst('*'); + parseOperatorInst(_Operator.multiplication); break; case _opDot: parseBuiltinFunction('dot'); break; + case _opFOrdEqual: + parseOperatorInst(_Operator.equality); + break; + case _opFUnordNotEqual: + parseOperatorInst(_Operator.inequality); + break; + case _opFOrdLessThan: + parseOperatorInst(_Operator.lessThan); + break; + case _opFOrdGreaterThan: + parseOperatorInst(_Operator.greaterThan); + break; + case _opFOrdLessThanEqual: + parseOperatorInst(_Operator.lessThanEqual); + break; + case _opFOrdGreaterThanEqual: + parseOperatorInst(_Operator.greaterThanEqual); + break; + case _opLogicalEqual: + parseOperatorInst(_Operator.equality); + break; + case _opLogicalNotEqual: + parseOperatorInst(_Operator.inequality); + break; + case _opLogicalOr: + parseOperatorInst(_Operator.or); + break; + case _opLogicalAnd: + parseOperatorInst(_Operator.and); + break; + case _opLogicalNot: + parseUnaryOperator(_Operator.not); + break; case _opLabel: opLabel(); break; + case _opBranch: + opBranch(); + break; + case _opBranchConditional: + opBranchConditional(); + break; + case _opLoopMerge: + opLoopMerge(); + break; + case _opSelectionMerge: + opSelectionMerge(); + break; case _opReturn: opReturn(); break; @@ -712,12 +779,12 @@ class _Transpiler { } void opConstantTrue() { - position++; // Skip type operand. + position++; // Skip type operand. constantTrue = readWord(); } void opConstantFalse() { - position++; // Skip type operand. + position++; // Skip type operand. constantFalse = readWord(); } @@ -767,7 +834,7 @@ class _Transpiler { throw failure('function $id has return type mismatch'); } - final _Function f = _Function(functionType, id); + final _Function f = _Function(this, functionType, id); functions[id] = f; currentFunction = f; } @@ -841,7 +908,9 @@ class _Transpiler { } return; case _storageClassFunction: - addToCurrentBlock(_StringInstruction('$type $name')); + // function variables are declared the first time a value is + // stored to them. + currentFunction!.declareVariable(id, typeId); return; default: throw failure('$storageClass is an unsupported Storage Class'); @@ -872,6 +941,31 @@ class _Transpiler { final int pointer = readWord(); final int object = readWord(); ref(object); + + // Variables belonging to the current function need to be declared if they + // haven't been already. + final _Variable? v = currentFunction!.variable(pointer); + if (v != null && !v.initialized) { + addToCurrentBlock(_Store( + pointer, + object, + shouldDeclare: true, + declarationType: v.type, + )); + v.initialized = true; + return; + } + + // Is this a compound assignment operation? (x += y) + final _Instruction? objInstruction = results[object]; + if (objInstruction is _BinaryOperator && + resolveId(objInstruction.a) == pointer && + _isCompoundAssignment(objInstruction.op)) { + addToCurrentBlock( + _CompoundAssignment(pointer, objInstruction.op, objInstruction.b)); + return; + } + addToCurrentBlock(_Store(pointer, object)); } @@ -958,15 +1052,8 @@ class _Transpiler { final int sampledImage = readWord(); final int coordinate = readWord(); ref(coordinate); - addToCurrentBlock(_ImageSampleImplicitLod(type, name, sampledImage, coordinate)); - } - - void opFNegate() { - final int type = readWord(); - final int name = readWord(); - final int operand = readWord(); - ref(operand); - addToCurrentBlock(_Negate(type, name, operand)); + addToCurrentBlock( + _ImageSampleImplicitLod(type, name, sampledImage, coordinate)); } void opLabel() { @@ -974,11 +1061,33 @@ class _Transpiler { currentBlock = currentFunction!.addBlock(id); } + void opBranch() { + currentBlock!.branch = readWord(); + currentBlock = null; + } + + void opBranchConditional() { + final _Block b = currentBlock!; + b.condition = readWord(); + b.truthyBlock = readWord(); + b.falseyBlock = readWord(); + } + + void opLoopMerge() { + final _Block b = currentBlock!; + b.mergeBlock = readWord(); + b.continueBlock = readWord(); + } + + void opSelectionMerge() { + currentBlock!.mergeBlock = readWord(); + } + void opReturn() { if (currentFunction!.name == entryPoint) { return; } else { - addToCurrentBlock(_StringInstruction('return')); + addToCurrentBlock(_Return()); } } @@ -988,14 +1097,22 @@ class _Transpiler { addToCurrentBlock(_ReturnValue(value)); } - void parseOperatorInst(String op) { + void parseUnaryOperator(_Operator op) { + final int type = readWord(); + final int name = readWord(); + final int operand = readWord(); + ref(operand); + addToCurrentBlock(_UnaryOperator(type, name, op, operand)); + } + + void parseOperatorInst(_Operator op) { final int type = readWord(); final int name = readWord(); final int a = readWord(); final int b = readWord(); ref(a); ref(b); - addToCurrentBlock(_Operator(type, name, op, a, b)); + addToCurrentBlock(_BinaryOperator(type, name, op, a, b)); } void parseBuiltinFunction(String functionName) { diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/BUILD.gn b/engine/src/flutter/lib/spirv/test/exception_shaders/BUILD.gn index 79d41321c53..e51ac3017ed 100644 --- a/engine/src/flutter/lib/spirv/test/exception_shaders/BUILD.gn +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/BUILD.gn @@ -10,6 +10,8 @@ if (enable_unittests) { tool = "//flutter/lib/spirv/test:spirv_assembler" sources = [ + "for_loop_does_not_branch_to_conditional.spvasm", + "for_loop_does_not_branch_to_merge.spvasm", "image_type_arrayed_must_be_zero.spvasm", "image_type_depth_must_be_zero.spvasm", "image_type_dimensionality_must_be_2D.spvasm", @@ -17,6 +19,9 @@ if (enable_unittests) { "image_type_must_be_float.spvasm", "image_type_previously_declared.spvasm", "image_type_sampled_must_be_one.spvasm", + "multiple_statements_in_for_loop_continue_block.spvasm", + "multiple_variables_in_for_loop_comparison.spvasm", + "no_store_in_for_loop_continue.spvasm", "sampled_image_type_image_type_not_declared.spvasm", "sampled_image_type_invalid_image_type.spvasm", "sampled_image_type_previously_declared.spvasm", diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/for_loop_does_not_branch_to_conditional.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/for_loop_does_not_branch_to_conditional.spvasm new file mode 100644 index 00000000000..0b4c094fb26 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/for_loop_does_not_branch_to_conditional.spvasm @@ -0,0 +1,74 @@ +; Copyright 2013 The Flutter Authors. All rights reserved. +; Use of this source code is governed by a BSD-style license that can be +; found in the LICENSE file. +; +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 38 +; Schema: 0 +; +; Block %14 branches unconditionally to a block with no conditional-branch +; despite declaring a LoopMerge. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %oColor "oColor" + OpName %i "i" + OpName %a "a" + OpDecorate %oColor Location 0 + OpDecorate %a Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Function_float = OpTypePointer Function %float + %float_10 = OpConstant %float 10 + %bool = OpTypeBool +%_ptr_UniformConstant_float = OpTypePointer UniformConstant %float + %a = OpVariable %_ptr_UniformConstant_float UniformConstant + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Output_float = OpTypePointer Output %float + %float_1 = OpConstant %float 1 + %uint_3 = OpConstant %uint 3 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_float Function + OpStore %oColor %11 + OpStore %i %float_0 + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %16 + %18 = OpLabel + %19 = OpLoad %float %i + %22 = OpFOrdLessThan %bool %19 %float_10 + OpBranchConditional %22 %15 %16 + %15 = OpLabel + %25 = OpLoad %float %a + %29 = OpAccessChain %_ptr_Output_float %oColor %uint_0 + %30 = OpLoad %float %29 + %31 = OpFAdd %float %30 %25 + %32 = OpAccessChain %_ptr_Output_float %oColor %uint_0 + OpStore %32 %31 + OpBranch %17 + %17 = OpLabel + %33 = OpLoad %float %i + %35 = OpFAdd %float %33 %float_1 + OpStore %i %35 + OpBranch %14 + %16 = OpLabel + %37 = OpAccessChain %_ptr_Output_float %oColor %uint_3 + OpStore %37 %float_1 + OpReturn + OpFunctionEnd diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/for_loop_does_not_branch_to_merge.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/for_loop_does_not_branch_to_merge.spvasm new file mode 100644 index 00000000000..49d7487057a --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/for_loop_does_not_branch_to_merge.spvasm @@ -0,0 +1,74 @@ +; Copyright 2013 The Flutter Authors. All rights reserved. +; Use of this source code is governed by a BSD-style license that can be +; found in the LICENSE file. +; +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 38 +; Schema: 0 +; +; The conditional following the LoopMerge on line 56 does not branch +; to the loop merge block %16. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %oColor "oColor" + OpName %i "i" + OpName %a "a" + OpDecorate %oColor Location 0 + OpDecorate %a Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Function_float = OpTypePointer Function %float + %float_10 = OpConstant %float 10 + %bool = OpTypeBool +%_ptr_UniformConstant_float = OpTypePointer UniformConstant %float + %a = OpVariable %_ptr_UniformConstant_float UniformConstant + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Output_float = OpTypePointer Output %float + %float_1 = OpConstant %float 1 + %uint_3 = OpConstant %uint 3 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_float Function + OpStore %oColor %11 + OpStore %i %float_0 + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + %19 = OpLoad %float %i + %22 = OpFOrdLessThan %bool %19 %float_10 + OpBranchConditional %22 %15 %17 + %15 = OpLabel + %25 = OpLoad %float %a + %29 = OpAccessChain %_ptr_Output_float %oColor %uint_0 + %30 = OpLoad %float %29 + %31 = OpFAdd %float %30 %25 + %32 = OpAccessChain %_ptr_Output_float %oColor %uint_0 + OpStore %32 %31 + OpBranch %17 + %17 = OpLabel + %33 = OpLoad %float %i + %35 = OpFAdd %float %33 %float_1 + OpStore %i %35 + OpBranch %14 + %16 = OpLabel + %37 = OpAccessChain %_ptr_Output_float %oColor %uint_3 + OpStore %37 %float_1 + OpReturn + OpFunctionEnd diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/multiple_statements_in_for_loop_continue_block.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/multiple_statements_in_for_loop_continue_block.spvasm new file mode 100644 index 00000000000..d4019f2351c --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/multiple_statements_in_for_loop_continue_block.spvasm @@ -0,0 +1,76 @@ +; Copyright 2013 The Flutter Authors. All rights reserved. +; Use of this source code is governed by a BSD-style license that can be +; found in the LICENSE file. +; +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 39 +; Schema: 0 +; +; Continue block %18 contains more than one OpStore instruction. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %oColor "oColor" + OpName %j "j" + OpName %i "i" + OpName %a "a" + OpDecorate %oColor Location 0 + OpDecorate %a Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Function_float = OpTypePointer Function %float + %float_10 = OpConstant %float 10 + %bool = OpTypeBool +%_ptr_UniformConstant_float = OpTypePointer UniformConstant %float + %a = OpVariable %_ptr_UniformConstant_float UniformConstant + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Output_float = OpTypePointer Output %float + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %j = OpVariable %_ptr_Function_float Function + %i = OpVariable %_ptr_Function_float Function + OpStore %oColor %11 + OpStore %j %float_0 + OpStore %i %float_0 + OpBranch %15 + %15 = OpLabel + OpLoopMerge %17 %18 None + OpBranch %19 + %19 = OpLabel + %20 = OpLoad %float %i + %23 = OpFOrdLessThan %bool %20 %float_10 + OpBranchConditional %23 %16 %17 + %16 = OpLabel + %26 = OpLoad %float %a + %30 = OpAccessChain %_ptr_Output_float %oColor %uint_0 + %31 = OpLoad %float %30 + %32 = OpFAdd %float %31 %26 + %33 = OpAccessChain %_ptr_Output_float %oColor %uint_0 + OpStore %33 %32 + OpBranch %18 + %18 = OpLabel + %34 = OpLoad %float %i + %36 = OpFAdd %float %34 %float_1 + OpStore %i %36 + %37 = OpLoad %float %j + %38 = OpFAdd %float %37 %float_1 + OpStore %j %38 + OpBranch %15 + %17 = OpLabel + OpReturn + OpFunctionEnd diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/multiple_variables_in_for_loop_comparison.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/multiple_variables_in_for_loop_comparison.spvasm new file mode 100644 index 00000000000..ebae022b646 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/multiple_variables_in_for_loop_comparison.spvasm @@ -0,0 +1,78 @@ +; Copyright 2013 The Flutter Authors. All rights reserved. +; Use of this source code is governed by a BSD-style license that can be +; found in the LICENSE file. +; +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 41 +; Schema: 0 +; +; Condition %27 depends on more than one variable, but is used as a for-loop +; condition. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %oColor "oColor" + OpName %i "i" + OpName %j "j" + OpName %a "a" + OpDecorate %oColor Location 0 + OpDecorate %a Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Function_float = OpTypePointer Function %float + %float_10 = OpConstant %float 10 + %bool = OpTypeBool + %float_5 = OpConstant %float 5 +%_ptr_UniformConstant_float = OpTypePointer UniformConstant %float + %a = OpVariable %_ptr_UniformConstant_float UniformConstant + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Output_float = OpTypePointer Output %float + %float_1 = OpConstant %float 1 + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_float Function + %j = OpVariable %_ptr_Function_float Function + OpStore %oColor %11 + OpStore %i %float_0 + OpStore %j %float_0 + OpBranch %15 + %15 = OpLabel + OpLoopMerge %17 %18 None + OpBranch %19 + %19 = OpLabel + %20 = OpLoad %float %i + %23 = OpFOrdLessThan %bool %20 %float_10 + %24 = OpLoad %float %j + %26 = OpFOrdLessThan %bool %24 %float_5 + %27 = OpLogicalAnd %bool %23 %26 + OpBranchConditional %27 %16 %17 + %16 = OpLabel + %30 = OpLoad %float %a + %34 = OpAccessChain %_ptr_Output_float %oColor %uint_0 + %35 = OpLoad %float %34 + %36 = OpFAdd %float %35 %30 + %37 = OpAccessChain %_ptr_Output_float %oColor %uint_0 + OpStore %37 %36 + OpBranch %18 + %18 = OpLabel + %38 = OpLoad %float %i + %40 = OpFAdd %float %38 %float_1 + OpStore %i %40 + OpBranch %15 + %17 = OpLabel + OpReturn + OpFunctionEnd diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/no_store_in_for_loop_continue.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/no_store_in_for_loop_continue.spvasm new file mode 100644 index 00000000000..cd7546e954f --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/no_store_in_for_loop_continue.spvasm @@ -0,0 +1,66 @@ +; Copyright 2013 The Flutter Authors. All rights reserved. +; Use of this source code is governed by a BSD-style license that can be +; found in the LICENSE file. +; +; SPIR-V +; Version: 1.0 +; Generator: Khronos Glslang Reference Front End; 10 +; Bound: 33 +; Schema: 0 +; +; Continue block %17 has no OpStore instruction. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %oColor "oColor" + OpName %i "i" + OpName %a "a" + OpDecorate %oColor Location 0 + OpDecorate %a Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %float_0 = OpConstant %float 0 + %11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0 +%_ptr_Function_float = OpTypePointer Function %float + %float_10 = OpConstant %float 10 + %bool = OpTypeBool +%_ptr_UniformConstant_float = OpTypePointer UniformConstant %float + %a = OpVariable %_ptr_UniformConstant_float UniformConstant + %uint = OpTypeInt 32 0 + %uint_0 = OpConstant %uint 0 +%_ptr_Output_float = OpTypePointer Output %float + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_float Function + OpStore %oColor %11 + OpStore %i %float_0 + OpBranch %14 + %14 = OpLabel + OpLoopMerge %16 %17 None + OpBranch %18 + %18 = OpLabel + %19 = OpLoad %float %i + %22 = OpFOrdLessThan %bool %19 %float_10 + OpBranchConditional %22 %15 %16 + %15 = OpLabel + %25 = OpLoad %float %a + %29 = OpAccessChain %_ptr_Output_float %oColor %uint_0 + %30 = OpLoad %float %29 + %31 = OpFAdd %float %30 %25 + %32 = OpAccessChain %_ptr_Output_float %oColor %uint_0 + OpStore %32 %31 + OpBranch %17 + %17 = OpLabel + OpBranch %14 + %16 = OpLabel + OpReturn + OpFunctionEnd diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/src.glsl b/engine/src/flutter/lib/spirv/test/exception_shaders/src.glsl new file mode 100644 index 00000000000..01f77787f64 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/src.glsl @@ -0,0 +1,13 @@ +#version 450 + +layout (location = 0) out vec4 oColor; + +layout (location = 0) uniform float a; + +void main() { + oColor = vec4(0); + for (float i = 0; i < 10.0; i++) { + oColor.r += a; + } + oColor.a = 1.0; +} diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/164_OpLogicalEqual.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/164_OpLogicalEqual.glsl new file mode 100644 index 00000000000..de93798337e --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/164_OpLogicalEqual.glsl @@ -0,0 +1,20 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (a >= 0.0 == a >= 1.0) { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } +} + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/165_OpLogicalNotEqual.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/165_OpLogicalNotEqual.glsl new file mode 100644 index 00000000000..803bd12470a --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/165_OpLogicalNotEqual.glsl @@ -0,0 +1,21 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (a <= 0.0 != a >= 1.0) { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } +} + + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/166_OpLogicalOr.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/166_OpLogicalOr.glsl new file mode 100644 index 00000000000..4b31d391d55 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/166_OpLogicalOr.glsl @@ -0,0 +1,20 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (a <= 0.0 || a == 1.0) { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } +} + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/167_OpLogicalAnd.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/167_OpLogicalAnd.glsl new file mode 100644 index 00000000000..55e7fd2eb03 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/167_OpLogicalAnd.glsl @@ -0,0 +1,20 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (a != 0.0 && a == 1.0) { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } +} + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/168_OpLogicalNot.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/168_OpLogicalNot.glsl new file mode 100644 index 00000000000..bec1109483b --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/168_OpLogicalNot.glsl @@ -0,0 +1,20 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (!(a == 0.0)) { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } +} + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/180_OpFOrdEqual.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/180_OpFOrdEqual.glsl new file mode 100644 index 00000000000..480080eb06f --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/180_OpFOrdEqual.glsl @@ -0,0 +1,20 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (a == 1.0) { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } +} + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/183_OpFUnordNotEqual.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/183_OpFUnordNotEqual.glsl new file mode 100644 index 00000000000..b49ff5986dc --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/183_OpFUnordNotEqual.glsl @@ -0,0 +1,20 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (a != 0.0) { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } +} + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/184_OpFOrdLessThan.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/184_OpFOrdLessThan.glsl new file mode 100644 index 00000000000..69992de3d1d --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/184_OpFOrdLessThan.glsl @@ -0,0 +1,20 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (a < 1.0) { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } else { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } +} + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/186_OpFOrdGreaterThan.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/186_OpFOrdGreaterThan.glsl new file mode 100644 index 00000000000..26e9753a63c --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/186_OpFOrdGreaterThan.glsl @@ -0,0 +1,20 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (a > 0.0) { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } +} + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/188_OpFOrdLessThanEqual.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/188_OpFOrdLessThanEqual.glsl new file mode 100644 index 00000000000..f0dcc0f281b --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/188_OpFOrdLessThanEqual.glsl @@ -0,0 +1,20 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (a <= 2.0 && a <= 1.0) { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } +} + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/190_OpFOrdGreaterThanEqual.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/190_OpFOrdGreaterThanEqual.glsl new file mode 100644 index 00000000000..0523b2e2986 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/190_OpFOrdGreaterThanEqual.glsl @@ -0,0 +1,20 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (a >= 0.0 && a >= 1.0) { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } +} + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/246_OpLoopMerge.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/246_OpLoopMerge.glsl new file mode 100644 index 00000000000..11c7d2a230f --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/246_OpLoopMerge.glsl @@ -0,0 +1,28 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + float sum = 0.0; + for (float i = 0.0; i < 6.0; i++) { + if (i > a * 5.0) { + break; + } + if (i < 1.0) { + continue; + } + if (a > 0.0) { + sum += a * 0.25; + } + } + fragColor = vec4(0.0, sum, 0.0, 1.0); +} + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/250_OpBranchConditional.glsl b/engine/src/flutter/lib/spirv/test/supported_op_shaders/250_OpBranchConditional.glsl new file mode 100644 index 00000000000..c72ea85af02 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/250_OpBranchConditional.glsl @@ -0,0 +1,24 @@ +#version 320 es + +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +precision highp float; + +layout(location = 0) out vec4 fragColor; + +layout(location = 0) uniform float a; + +void main() { + if (a > 0.0) { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + if (a < 0.5) { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } + } else { + fragColor = vec4(0.0, 0.0, 0.0, 1.0); + } +} + + diff --git a/engine/src/flutter/lib/spirv/test/supported_op_shaders/BUILD.gn b/engine/src/flutter/lib/spirv/test/supported_op_shaders/BUILD.gn index b6751f67c0f..bb85ce01bf9 100644 --- a/engine/src/flutter/lib/spirv/test/supported_op_shaders/BUILD.gn +++ b/engine/src/flutter/lib/spirv/test/supported_op_shaders/BUILD.gn @@ -19,12 +19,25 @@ if (enable_unittests) { "145_OpMatrixTimesVector.glsl", "146_OpMatrixTimesMatrix.glsl", "148_OpDot.glsl", + "164_OpLogicalEqual.glsl", + "165_OpLogicalNotEqual.glsl", + "166_OpLogicalOr.glsl", + "167_OpLogicalAnd.glsl", + "168_OpLogicalNot.glsl", + "180_OpFOrdEqual.glsl", + "183_OpFUnordNotEqual.glsl", + "184_OpFOrdLessThan.glsl", + "186_OpFOrdGreaterThan.glsl", + "188_OpFOrdLessThanEqual.glsl", + "190_OpFOrdGreaterThanEqual.glsl", "19_OpTypeVoid.glsl", "20_OpTypeBool.glsl", "21_OpTypeInt.glsl", "22_OpTypeFloat.glsl", "23_OpTypeVector.glsl", + "246_OpLoopMerge.glsl", "24_OpTypeMatrix.glsl", + "250_OpBranchConditional.glsl", "33_OpTypeFunction.glsl", ]