SPIR-V Transpiler Control Flow (flutter/engine#31451)

This commit is contained in:
Christopher Crawford 2022-03-02 14:06:08 -05:00 committed by GitHub
parent 36fa2a0924
commit e7ddebd2cb
27 changed files with 1340 additions and 104 deletions

View File

@ -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.

View File

@ -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,
);
}

View File

@ -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<int, int> _glslStd450OpArgc = <int, int>{
_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);
}

View File

@ -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<int>.filled(type.params.length, 0);
final _Transpiler transpiler;
final int name;
final _FunctionType type;
final List<int> params;
_Function(this.type, this.name) :
params = List<int>.filled(type.params.length, 0);
// entry point for the function
_Block? entry;
@ -20,46 +31,76 @@ class _Function {
final Map<int, _Block> blocks = <int, _Block>{};
final List<int> deps = <int>[];
final Map<int, _Variable> variables = <int, _Variable>{};
_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<int> deps = <int>{};
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();
}
}

View File

@ -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<int> get deps => <int>[];
// 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<int> args;
_FunctionCall(this.type, this.id, this.function, this.args);
@override
List<int> 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<int> get deps => <int>[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<int> get deps => <int>[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<int> indices;
_AccessChain(this.type, this.id, this.base, this.indices);
@override
List<int> get deps => <int>[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<int> indices;
_VectorShuffle(this.type, this.id, this.vector, this.indices);
@override
List<int> get deps => <int>[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<int> components;
_CompositeConstruct(this.type, this.id, this.components);
@override
List<int> 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<int> indices;
_CompositeExtract(this.type, this.id, this.src, this.indices);
@override
List<int> get deps => <int>[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<int> get deps => <int>[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<int> get deps => <int>[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<int> get deps => <int>[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<int> get deps => <int>[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<int> args;
_BuiltinFunction(this.type, this.id, this.function, this.args);
@override
List<int> 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<int> get deps => <int>[value];
@override
void write(_Transpiler t, StringBuffer out) {

View File

@ -201,6 +201,23 @@ class _Transpiler {
TranspileException failure(String why) =>
TranspileException._(currentOp, why);
void collectDeps(Set<int> 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<int> 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) {

View File

@ -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",

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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",
]