diff --git a/engine/src/flutter/lib/spirv/README.md b/engine/src/flutter/lib/spirv/README.md index 2c3ee0198b..797067ceb3 100644 --- a/engine/src/flutter/lib/spirv/README.md +++ b/engine/src/flutter/lib/spirv/README.md @@ -22,14 +22,14 @@ the code will need to adhere to the following rules. - Control flow is prohibited aside from function calls and `return`. `if`, `while`, `for`, `switch`, etc. - No inputs from other shader stages. -- Only float, float-vector types, and square float-matrix types. +- Only sampler2D, 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. -- Debug symbols must be stripped, you can use the `spirv-opt` `--strip-debug` flag. +- Only the `texture` function is supported for sampling from a sampler2D object. These rules may become less strict in future versions. Conformant SPIR-V should successfully transpile from the current version onwards. In other words, a SPIR-V shader you use now that meets these rules should keep working, but the output of the transpiler may change for that shader. -Support for textures, control flow, and structured types is planned, but not currently included. +Support for control flow, and structured types is planned, but not currently included. ## Testing diff --git a/engine/src/flutter/lib/spirv/lib/spirv.dart b/engine/src/flutter/lib/spirv/lib/spirv.dart index 7034c284b8..352e6262df 100644 --- a/engine/src/flutter/lib/spirv/lib/spirv.dart +++ b/engine/src/flutter/lib/spirv/lib/spirv.dart @@ -38,7 +38,15 @@ class TranspileResult { /// The number of float uniforms used in this shader. final int uniformFloatCount; - TranspileResult._(this.src, this.uniformFloatCount, this.language); + /// The number of samplers (children) used in this shader. + final int samplerCount; + + TranspileResult._( + this.src, + this.uniformFloatCount, + this.samplerCount, + this.language, + ); } /// Thrown during transpilation due to malformed or unsupported SPIR-V. @@ -64,6 +72,7 @@ TranspileResult transpile(ByteBuffer spirv, TargetLanguage target) { return TranspileResult._( t.src.toString(), t.uniformFloatCount, + t.samplerCount, target, ); } diff --git a/engine/src/flutter/lib/spirv/lib/src/constants.dart b/engine/src/flutter/lib/spirv/lib/src/constants.dart index 21569d2430..a4c5a52f28 100644 --- a/engine/src/flutter/lib/spirv/lib/src/constants.dart +++ b/engine/src/flutter/lib/spirv/lib/src/constants.dart @@ -34,6 +34,9 @@ const int _decorationLocation = 30; // Explicitly supported builtin types const int _builtinFragCoord = 15; +// Explicitly supported dimensionalities +const int _dim2D = 1; + // Ops that have no semantic meaning in output and can be safely ignored const int _opSource = 3; const int _opSourceExtension = 4; @@ -55,6 +58,8 @@ const int _opTypeInt = 21; const int _opTypeFloat = 22; const int _opTypeVector = 23; const int _opTypeMatrix = 24; +const int _opTypeImage = 25; +const int _opTypeSampledImage = 27; const int _opTypePointer = 32; const int _opTypeFunction = 33; const int _opConstantTrue = 41; @@ -74,6 +79,8 @@ const int _opDecorate = 71; const int _opVectorShuffle = 79; const int _opCompositeConstruct = 80; const int _opCompositeExtract = 81; +const int _opImageSampleImplicitLod = 87; +const int _opImageQuerySize = 104; const int _opConvertFToS = 110; const int _opConvertSToF = 111; const int _opFNegate = 127; diff --git a/engine/src/flutter/lib/spirv/lib/src/transpiler.dart b/engine/src/flutter/lib/spirv/lib/src/transpiler.dart index 8fa9636fa8..999ba82a34 100644 --- a/engine/src/flutter/lib/spirv/lib/src/transpiler.dart +++ b/engine/src/flutter/lib/spirv/lib/src/transpiler.dart @@ -51,7 +51,7 @@ const String _mainFunctionName = 'main'; /// https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#OpConstant class _Transpiler { _Transpiler(this.spirv, this.target) { - out = src; + out = header; } final Uint32List spirv; @@ -60,6 +60,23 @@ class _Transpiler { /// The resulting source code of the target language is written to src. final StringBuffer src = StringBuffer(); + /// Contains shader header, constants, and uniforms. + final StringBuffer header = StringBuffer(); + + /// The main body of code, contains function definitions. + final StringBuffer body = StringBuffer(); + + /// Uniform declarations. + final Map uniformDeclarations = {}; + + /// Declarations for sampler sizes in SkSL. + /// + /// This is because the SkSL eval function uses texel coordinates when + /// sampling an ImageShader, and SkSL does not support the textureSize + /// function. These uniforms allow adding support for normalized + /// coordinates for [opImageSampleImplicitLod]. + final Map samplerSizeDeclarations = {}; + /// ID mapped to numerical types. final Map types = {}; @@ -80,6 +97,9 @@ class _Transpiler { /// ID mapped to ID. Used by [OpLoad]. final Map alias = {}; + /// ID mapped to a string to use instead of a generated name. + final Map nameOverloads = {}; + /// The ID for a constant true value. /// See [opConstantTrue]. int constantTrue = 0; @@ -113,6 +133,14 @@ class _Transpiler { /// See [opTypeFloat]. int floatType = 0; + /// The ID of the image type. + /// See [opTypeImage]. + int imageType = 0; + + /// The ID of the sampledImage type. + /// See [opTypeSampledImage]. + int sampledImageType = 0; + /// The ID of the function that is currently being defined. /// Set by [opFunction] and unset by [opFunctionEnd]. int currentFunction = 0; @@ -131,9 +159,12 @@ class _Transpiler { /// Set by [opDecorate]. int fragCoord = 0; - /// The number of floats used by uniform + /// The number of floats used by uniforms. int uniformFloatCount = 0; + /// The number of samplers used by uniforms. + int samplerCount = 0; + /// Current indentation to prepend to new lines. String indent = ''; @@ -147,6 +178,9 @@ class _Transpiler { void transpile() { parseHeader(); writeHeader(); + + // Parse instructions and write to body. + out = body; while (position < spirv.length) { final int lastPosition = position; parseInstruction(); @@ -154,10 +188,33 @@ class _Transpiler { assert(position > lastPosition); } - src.writeln(); // TODO(antrob): Investigate if `List.filled(maxFunctionId, false)` can be used here instead. final Set visited = {}; writeFunctionAndDeps(visited, entryPoint); + + // Add uniform declarations to header. + if (uniformDeclarations.isNotEmpty) { + header.writeln(); + final List locations = uniformDeclarations.keys.toList(); + locations.sort((int a, int b) => a - b); + for (final int location in locations) { + header.writeln(uniformDeclarations[location]); + } + } + + // Add SkSL sampler size declarations to header. + if (samplerSizeDeclarations.isNotEmpty) { + header.writeln(); + final List locations = samplerSizeDeclarations.keys.toList(); + locations.sort((int a, int b) => a - b); + for (final int location in locations) { + header.writeln(samplerSizeDeclarations[location]); + } + } + + src.write(header); + src.writeln(); + src.write(body); } TranspileException failure(String why) => @@ -171,19 +228,19 @@ class _Transpiler { for (final int dep in functionDeps[function]!) { writeFunctionAndDeps(visited, dep); } - src.write(functionDefs[function]!.toString()); + out.write(functionDefs[function]!.toString()); } void writeHeader() { switch (target) { case TargetLanguage.glslES: - src.writeln('#version 100\n'); - src.writeln('precision mediump float;\n'); + out.writeln('#version 100\n'); + out.writeln('precision mediump float;\n'); break; case TargetLanguage.glslES300: - src.writeln('#version 300 es\n'); - src.writeln('precision mediump float;\n'); - src.writeln('layout ( location = 0 ) out vec4 $_colorVariableName;\n'); + out.writeln('#version 300 es\n'); + out.writeln('precision mediump float;\n'); + out.writeln('layout ( location = 0 ) out vec4 $_colorVariableName;\n'); break; default: break; @@ -193,11 +250,14 @@ class _Transpiler { String resolveName(int id) { if (alias.containsKey(id)) { return resolveName(alias[id]!); + } + if (nameOverloads.containsKey(id)) { + return nameOverloads[id]!; } else if (constantTrue > 0 && id == constantTrue) { return 'true'; } else if (constantFalse > 0 && id == constantFalse) { return 'false'; - } else if (id == colorOutput) { + } if (id == colorOutput) { if (target == TargetLanguage.glslES) { return _glslESColorName; } else { @@ -259,6 +319,7 @@ class _Transpiler { out.writeln('$indent$type $name = $type($value);'); } + /// Read an instruction word, and handle the operation. /// /// SPIR-V instructions contain an op-code as well as a @@ -309,6 +370,12 @@ class _Transpiler { case _opTypeMatrix: opTypeMatrix(); break; + case _opTypeImage: + opTypeImage(); + break; + case _opTypeSampledImage: + opTypeSampledImage(); + break; case _opTypePointer: opTypePointer(); break; @@ -369,6 +436,9 @@ class _Transpiler { case _opCompositeExtract: opCompositeExtract(); break; + case _opImageSampleImplicitLod: + opImageSampleImplicitLod(); + break; case _opFNegate: opFNegate(); break; @@ -557,6 +627,54 @@ class _Transpiler { types[id] = t; } + void opTypeImage() { + if (imageType != 0) { + throw failure('Image type was previously declared.'); + } + final int id = readWord(); + final int sampledType = readWord(); + if (types[sampledType] != _Type.float) { + throw failure('Sampled type must be float.'); + } + final int dimensionality = readWord(); + if (dimensionality != _dim2D) { + throw failure('Dimensionality must be 2D.'); + } + final int depth = readWord(); + if (depth != 0) { + throw failure('Depth must be 0.'); + } + final int arrayed = readWord(); + if (arrayed != 0) { + throw failure('Arrayed must be 0.'); + } + final int multisampled = readWord(); + if (multisampled != 0) { + throw failure('Multisampled must be 0.'); + } + final int sampled = readWord(); + if (sampled != 1) { + throw failure('Sampled must be 1.'); + } + imageType = id; + } + + void opTypeSampledImage() { + if (sampledImageType != 0) { + throw failure('imageSampledType was previously declared.'); + } + if (imageType == 0) { + throw failure('imageType has not yet been declared.'); + } + final int id = readWord(); + final int imgType = readWord(); + if (imgType != imageType) { + throw failure('Invalid image type.'); + } + sampledImageType = id; + types[id] = _Type.sampledImage; + } + void opTypePointer() { final int id = readWord(); // ignore storage class @@ -580,14 +698,12 @@ class _Transpiler { } void opConstantTrue() { - // Skip type operand. - position++; + position++; // Skip type operand. constantTrue = readWord(); } void opConstantFalse() { - // Skip type operand. - position++; + position++; // Skip type operand. constantFalse = readWord(); } @@ -604,21 +720,21 @@ class _Transpiler { valueString = '$v'; } final String typeName = resolveType(type); - src.writeln('const $typeName $id = $valueString;'); + header.writeln('const $typeName $id = $valueString;'); } void opConstantComposite() { final String type = resolveType(readWord()); final String id = resolveName(readWord()); - src.write('const $type $id = $type('); + header.write('const $type $id = $type('); final int count = nextPosition - position; for (int i = 0; i < count; i++) { - src.write(resolveName(readWord())); + header.write(resolveName(readWord())); if (i < count - 1) { - src.write(', '); + header.write(', '); } } - src.writeln(');'); + header.writeln(');'); } void opFunction() { @@ -685,7 +801,7 @@ class _Transpiler { // Remove trailing two space characters, if present. indent = indent.substring(0, max(0, indent.length - 2)); currentFunction = 0; - out = src; + out = body; currentFunctionType = null; } @@ -721,16 +837,27 @@ class _Transpiler { switch (storageClass) { case _storageClassUniformConstant: - if (target == TargetLanguage.glslES300) { - final String location = locations[id].toString(); - src.write('layout ( location = $location ) '); + int? location = locations[id]; + if (location == null) { + throw failure('$id had no location specified'); } - src.writeln('uniform $type $name;'); + String prefix = ''; + if (target == TargetLanguage.glslES300) { + prefix = 'layout ( location = $location ) '; + } + uniformDeclarations[location] = '${prefix}uniform $type $name;'; final _Type? t = types[typeId]; if (t == null) { throw failure('$typeId is not a defined type'); } - uniformFloatCount += _typeFloatCounts[t]!; + if (t == _Type.sampledImage) { + samplerCount++; + if (target == TargetLanguage.sksl) { + samplerSizeDeclarations[location] = 'uniform half2 ${name}_size;'; + } + } else { + uniformFloatCount += _typeFloatCounts[t]!; + } return; case _storageClassInput: return; @@ -772,7 +899,7 @@ class _Transpiler { void opAccessChain() { final String type = resolveType(readWord()); - final String name = resolveName(readWord()); + final int id = readWord(); final String base = resolveName(readWord()); // opAccessChain currently only supports indexed access. @@ -780,13 +907,13 @@ class _Transpiler { // Currently, structs will be caught before this method is called, // since using the instruction to define a struct type will throw // an exception. - out.write('$indent$type $name = $base'); + String overload = base; final int count = nextPosition - position; for (int i = 0; i < count; i++) { final String index = resolveName(readWord()); - out.write('[$index]'); + overload += '[$index]'; } - out.writeln(';'); + nameOverloads[id] = overload; } void opDecorate() { @@ -853,6 +980,18 @@ class _Transpiler { out.writeln(';'); } + void opImageSampleImplicitLod() { + final String type = resolveType(readWord()); + final String name = resolveName(readWord()); + final String sampledImage = resolveName(readWord()); + final String coordinate = resolveName(readWord()); + if (target == TargetLanguage.sksl) { + out.writeln('$indent$type $name = $sampledImage.eval(${sampledImage}_size * $coordinate);'); + } else { + out.writeln('$indent$type $name = texture($sampledImage, $coordinate);'); + } + } + void opFNegate() { final String type = resolveType(readWord()); final String name = resolveName(readWord()); diff --git a/engine/src/flutter/lib/spirv/lib/src/types.dart b/engine/src/flutter/lib/spirv/lib/src/types.dart index 3fd3dde5c2..e23ada3d7e 100644 --- a/engine/src/flutter/lib/spirv/lib/src/types.dart +++ b/engine/src/flutter/lib/spirv/lib/src/types.dart @@ -15,6 +15,7 @@ enum _Type { float2x2, float3x3, float4x4, + sampledImage, } class _FunctionType { @@ -47,6 +48,7 @@ const Map<_Type, String> _skslTypeNames = <_Type, String>{ _Type.float2x2: 'float2x2', _Type.float3x3: 'float3x3', _Type.float4x4: 'float4x4', + _Type.sampledImage: 'shader', }; const Map<_Type, String> _glslTypeNames = <_Type, String>{ @@ -60,6 +62,7 @@ const Map<_Type, String> _glslTypeNames = <_Type, String>{ _Type.float2x2: 'mat2', _Type.float3x3: 'mat3', _Type.float4x4: 'mat4', + _Type.sampledImage: 'sampler2D', }; const Map<_Type, int> _typeFloatCounts = <_Type, int>{ 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 8cdc9b8fa3..79d41321c5 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,16 @@ if (enable_unittests) { tool = "//flutter/lib/spirv/test:spirv_assembler" sources = [ + "image_type_arrayed_must_be_zero.spvasm", + "image_type_depth_must_be_zero.spvasm", + "image_type_dimensionality_must_be_2D.spvasm", + "image_type_multisampled_must_be_zero.spvasm", + "image_type_must_be_float.spvasm", + "image_type_previously_declared.spvasm", + "image_type_sampled_must_be_one.spvasm", + "sampled_image_type_image_type_not_declared.spvasm", + "sampled_image_type_invalid_image_type.spvasm", + "sampled_image_type_previously_declared.spvasm", "unassigned_function_type.spvasm", "unassigned_pointer_type.spvasm", "unassigned_type.spvasm", diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_arrayed_must_be_zero.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_arrayed_must_be_zero.spvasm new file mode 100644 index 0000000000..f81daf936b --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_arrayed_must_be_zero.spvasm @@ -0,0 +1,62 @@ +; 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; 8 +; Bound: 28 +; Schema: 0 +; +; Line 43 contains a non-zero-arrayed image type. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %iResolution "iResolution" + OpName %oColor "oColor" + OpName %iImage "iImage" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %iResolution Location 1 + OpDecorate %oColor Location 0 + OpDecorate %iImage Location 0 + OpDecorate %iImage DescriptorSet 0 + OpDecorate %iImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_UniformConstant_v2float = OpTypePointer UniformConstant %v2float +%iResolution = OpVariable %_ptr_UniformConstant_v2float UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %21 = OpTypeImage %float 2D 0 1 0 1 Unknown + %22 = OpTypeSampledImage %21 +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 + %iImage = OpVariable %_ptr_UniformConstant_22 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %13 = OpLoad %v4float %gl_FragCoord + %14 = OpVectorShuffle %v2float %13 %13 0 1 + %17 = OpLoad %v2float %iResolution + %18 = OpFDiv %v2float %14 %17 + OpStore %uv %18 + %25 = OpLoad %22 %iImage + %26 = OpLoad %v2float %uv + %27 = OpImageSampleImplicitLod %v4float %25 %26 + OpStore %oColor %27 + OpReturn + OpFunctionEnd + + diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_depth_must_be_zero.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_depth_must_be_zero.spvasm new file mode 100644 index 0000000000..7205a053df --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_depth_must_be_zero.spvasm @@ -0,0 +1,62 @@ +; 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; 8 +; Bound: 28 +; Schema: 0 +; +; Line 43 contains a non-zero-depth image type. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %iResolution "iResolution" + OpName %oColor "oColor" + OpName %iImage "iImage" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %iResolution Location 1 + OpDecorate %oColor Location 0 + OpDecorate %iImage Location 0 + OpDecorate %iImage DescriptorSet 0 + OpDecorate %iImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_UniformConstant_v2float = OpTypePointer UniformConstant %v2float +%iResolution = OpVariable %_ptr_UniformConstant_v2float UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %21 = OpTypeImage %float 2D 1 0 0 1 Unknown + %22 = OpTypeSampledImage %21 +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 + %iImage = OpVariable %_ptr_UniformConstant_22 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %13 = OpLoad %v4float %gl_FragCoord + %14 = OpVectorShuffle %v2float %13 %13 0 1 + %17 = OpLoad %v2float %iResolution + %18 = OpFDiv %v2float %14 %17 + OpStore %uv %18 + %25 = OpLoad %22 %iImage + %26 = OpLoad %v2float %uv + %27 = OpImageSampleImplicitLod %v4float %25 %26 + OpStore %oColor %27 + OpReturn + OpFunctionEnd + + diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_dimensionality_must_be_2D.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_dimensionality_must_be_2D.spvasm new file mode 100644 index 0000000000..3bbd01180b --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_dimensionality_must_be_2D.spvasm @@ -0,0 +1,61 @@ +; 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; 8 +; Bound: 28 +; Schema: 0 +; +; Line 43 contains a non-2D image type. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %iResolution "iResolution" + OpName %oColor "oColor" + OpName %iImage "iImage" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %iResolution Location 1 + OpDecorate %oColor Location 0 + OpDecorate %iImage Location 0 + OpDecorate %iImage DescriptorSet 0 + OpDecorate %iImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_UniformConstant_v2float = OpTypePointer UniformConstant %v2float +%iResolution = OpVariable %_ptr_UniformConstant_v2float UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %21 = OpTypeImage %float 3D 0 0 0 1 Unknown + %22 = OpTypeSampledImage %21 +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 + %iImage = OpVariable %_ptr_UniformConstant_22 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %13 = OpLoad %v4float %gl_FragCoord + %14 = OpVectorShuffle %v2float %13 %13 0 1 + %17 = OpLoad %v2float %iResolution + %18 = OpFDiv %v2float %14 %17 + OpStore %uv %18 + %25 = OpLoad %22 %iImage + %26 = OpLoad %v2float %uv + %27 = OpImageSampleImplicitLod %v4float %25 %26 + OpStore %oColor %27 + OpReturn + OpFunctionEnd + diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_multisampled_must_be_zero.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_multisampled_must_be_zero.spvasm new file mode 100644 index 0000000000..0bf1a6a177 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_multisampled_must_be_zero.spvasm @@ -0,0 +1,61 @@ +; 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; 8 +; Bound: 28 +; Schema: 0 +; +; Line 43 contains a non-zero-multisampled image type. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %iResolution "iResolution" + OpName %oColor "oColor" + OpName %iImage "iImage" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %iResolution Location 1 + OpDecorate %oColor Location 0 + OpDecorate %iImage Location 0 + OpDecorate %iImage DescriptorSet 0 + OpDecorate %iImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_UniformConstant_v2float = OpTypePointer UniformConstant %v2float +%iResolution = OpVariable %_ptr_UniformConstant_v2float UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %21 = OpTypeImage %float 2D 0 0 1 1 Unknown + %22 = OpTypeSampledImage %21 +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 + %iImage = OpVariable %_ptr_UniformConstant_22 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %13 = OpLoad %v4float %gl_FragCoord + %14 = OpVectorShuffle %v2float %13 %13 0 1 + %17 = OpLoad %v2float %iResolution + %18 = OpFDiv %v2float %14 %17 + OpStore %uv %18 + %25 = OpLoad %22 %iImage + %26 = OpLoad %v2float %uv + %27 = OpImageSampleImplicitLod %v4float %25 %26 + OpStore %oColor %27 + OpReturn + OpFunctionEnd + diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_must_be_float.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_must_be_float.spvasm new file mode 100644 index 0000000000..f16eb17962 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_must_be_float.spvasm @@ -0,0 +1,61 @@ +; 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; 8 +; Bound: 28 +; Schema: 0 +; +; Line 43 contains a non-float image type. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %iResolution "iResolution" + OpName %oColor "oColor" + OpName %iImage "iImage" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %iResolution Location 1 + OpDecorate %oColor Location 0 + OpDecorate %iImage Location 0 + OpDecorate %iImage DescriptorSet 0 + OpDecorate %iImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_UniformConstant_v2float = OpTypePointer UniformConstant %v2float +%iResolution = OpVariable %_ptr_UniformConstant_v2float UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %21 = OpTypeImage %void 2D 0 0 0 1 Unknown + %22 = OpTypeSampledImage %21 +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 + %iImage = OpVariable %_ptr_UniformConstant_22 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %13 = OpLoad %v4float %gl_FragCoord + %14 = OpVectorShuffle %v2float %13 %13 0 1 + %17 = OpLoad %v2float %iResolution + %18 = OpFDiv %v2float %14 %17 + OpStore %uv %18 + %25 = OpLoad %22 %iImage + %26 = OpLoad %v2float %uv + %27 = OpImageSampleImplicitLod %v4float %25 %26 + OpStore %oColor %27 + OpReturn + OpFunctionEnd + diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_previously_declared.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_previously_declared.spvasm new file mode 100644 index 0000000000..9003a3abb5 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_previously_declared.spvasm @@ -0,0 +1,62 @@ +; 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; 8 +; Bound: 29 +; Schema: 0 +; +; Line 45 contains a second OpTypeImage declaration. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %iResolution "iResolution" + OpName %oColor "oColor" + OpName %iImage "iImage" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %iResolution Location 1 + OpDecorate %oColor Location 0 + OpDecorate %iImage Location 0 + OpDecorate %iImage DescriptorSet 0 + OpDecorate %iImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_UniformConstant_v2float = OpTypePointer UniformConstant %v2float +%iResolution = OpVariable %_ptr_UniformConstant_v2float UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %21 = OpTypeImage %float 2D 0 0 0 1 Unknown + %28 = OpTypeImage %float 2D 0 0 0 1 Unknown + %22 = OpTypeSampledImage %21 +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 + %iImage = OpVariable %_ptr_UniformConstant_22 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %13 = OpLoad %v4float %gl_FragCoord + %14 = OpVectorShuffle %v2float %13 %13 0 1 + %17 = OpLoad %v2float %iResolution + %18 = OpFDiv %v2float %14 %17 + OpStore %uv %18 + %25 = OpLoad %22 %iImage + %26 = OpLoad %v2float %uv + %27 = OpImageSampleImplicitLod %v4float %25 %26 + OpStore %oColor %27 + OpReturn + OpFunctionEnd + diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_sampled_must_be_one.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_sampled_must_be_one.spvasm new file mode 100644 index 0000000000..93527c72f9 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/image_type_sampled_must_be_one.spvasm @@ -0,0 +1,63 @@ +; 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; 8 +; Bound: 28 +; Schema: 0 +; +; Line 43 contains an image type with 'sampled' not set to one. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %iResolution "iResolution" + OpName %oColor "oColor" + OpName %iImage "iImage" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %iResolution Location 1 + OpDecorate %oColor Location 0 + OpDecorate %iImage Location 0 + OpDecorate %iImage DescriptorSet 0 + OpDecorate %iImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_UniformConstant_v2float = OpTypePointer UniformConstant %v2float +%iResolution = OpVariable %_ptr_UniformConstant_v2float UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %21 = OpTypeImage %float 2D 0 0 0 0 Unknown + %22 = OpTypeSampledImage %21 +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 + %iImage = OpVariable %_ptr_UniformConstant_22 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %13 = OpLoad %v4float %gl_FragCoord + %14 = OpVectorShuffle %v2float %13 %13 0 1 + %17 = OpLoad %v2float %iResolution + %18 = OpFDiv %v2float %14 %17 + OpStore %uv %18 + %25 = OpLoad %22 %iImage + %26 = OpLoad %v2float %uv + %27 = OpImageSampleImplicitLod %v4float %25 %26 + OpStore %oColor %27 + OpReturn + OpFunctionEnd + + + diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/sampled_image_type_image_type_not_declared.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/sampled_image_type_image_type_not_declared.spvasm new file mode 100644 index 0000000000..4bde3531e1 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/sampled_image_type_image_type_not_declared.spvasm @@ -0,0 +1,63 @@ +; 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; 8 +; Bound: 29 +; Schema: 0 +; +; Line 43 contains an OpTypeSampledImage before OpImageType has been declared. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %iResolution "iResolution" + OpName %oColor "oColor" + OpName %iImage "iImage" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %iResolution Location 1 + OpDecorate %oColor Location 0 + OpDecorate %iImage Location 0 + OpDecorate %iImage DescriptorSet 0 + OpDecorate %iImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_UniformConstant_v2float = OpTypePointer UniformConstant %v2float +%iResolution = OpVariable %_ptr_UniformConstant_v2float UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %22 = OpTypeSampledImage %21 + %21 = OpTypeImage %float 2D 0 0 0 1 Unknown +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 + %iImage = OpVariable %_ptr_UniformConstant_22 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %13 = OpLoad %v4float %gl_FragCoord + %14 = OpVectorShuffle %v2float %13 %13 0 1 + %17 = OpLoad %v2float %iResolution + %18 = OpFDiv %v2float %14 %17 + OpStore %uv %18 + %25 = OpLoad %22 %iImage + %26 = OpLoad %v2float %uv + %27 = OpImageSampleImplicitLod %v4float %25 %26 + OpStore %oColor %27 + OpReturn + OpFunctionEnd + + + diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/sampled_image_type_invalid_image_type.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/sampled_image_type_invalid_image_type.spvasm new file mode 100644 index 0000000000..c13f5e3382 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/sampled_image_type_invalid_image_type.spvasm @@ -0,0 +1,62 @@ +; 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; 8 +; Bound: 29 +; Schema: 0 +; +; Line 44 contains an OpTypeSampledImage with an invalid image type. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %iResolution "iResolution" + OpName %oColor "oColor" + OpName %iImage "iImage" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %iResolution Location 1 + OpDecorate %oColor Location 0 + OpDecorate %iImage Location 0 + OpDecorate %iImage DescriptorSet 0 + OpDecorate %iImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_UniformConstant_v2float = OpTypePointer UniformConstant %v2float +%iResolution = OpVariable %_ptr_UniformConstant_v2float UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %21 = OpTypeImage %float 2D 0 0 0 1 Unknown + %22 = OpTypeSampledImage %float +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 + %iImage = OpVariable %_ptr_UniformConstant_22 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %13 = OpLoad %v4float %gl_FragCoord + %14 = OpVectorShuffle %v2float %13 %13 0 1 + %17 = OpLoad %v2float %iResolution + %18 = OpFDiv %v2float %14 %17 + OpStore %uv %18 + %25 = OpLoad %22 %iImage + %26 = OpLoad %v2float %uv + %27 = OpImageSampleImplicitLod %v4float %25 %26 + OpStore %oColor %27 + OpReturn + OpFunctionEnd + + diff --git a/engine/src/flutter/lib/spirv/test/exception_shaders/sampled_image_type_previously_declared.spvasm b/engine/src/flutter/lib/spirv/test/exception_shaders/sampled_image_type_previously_declared.spvasm new file mode 100644 index 0000000000..4628630177 --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/exception_shaders/sampled_image_type_previously_declared.spvasm @@ -0,0 +1,62 @@ +; 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; 8 +; Bound: 29 +; Schema: 0 +; +; Line 45 contains a second OpTypeSampledImage declaration. +; + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %oColor + OpExecutionMode %main OriginLowerLeft + OpSource GLSL 450 + OpName %main "main" + OpName %uv "uv" + OpName %gl_FragCoord "gl_FragCoord" + OpName %iResolution "iResolution" + OpName %oColor "oColor" + OpName %iImage "iImage" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %iResolution Location 1 + OpDecorate %oColor Location 0 + OpDecorate %iImage Location 0 + OpDecorate %iImage DescriptorSet 0 + OpDecorate %iImage Binding 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Function_v2float = OpTypePointer Function %v2float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%_ptr_UniformConstant_v2float = OpTypePointer UniformConstant %v2float +%iResolution = OpVariable %_ptr_UniformConstant_v2float UniformConstant +%_ptr_Output_v4float = OpTypePointer Output %v4float + %oColor = OpVariable %_ptr_Output_v4float Output + %21 = OpTypeImage %float 2D 0 0 0 1 Unknown + %22 = OpTypeSampledImage %21 + %28 = OpTypeSampledImage %21 +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 + %iImage = OpVariable %_ptr_UniformConstant_22 UniformConstant + %main = OpFunction %void None %3 + %5 = OpLabel + %uv = OpVariable %_ptr_Function_v2float Function + %13 = OpLoad %v4float %gl_FragCoord + %14 = OpVectorShuffle %v2float %13 %13 0 1 + %17 = OpLoad %v2float %iResolution + %18 = OpFDiv %v2float %14 %17 + OpStore %uv %18 + %25 = OpLoad %22 %iImage + %26 = OpLoad %v2float %uv + %27 = OpImageSampleImplicitLod %v4float %25 %26 + OpStore %oColor %27 + OpReturn + OpFunctionEnd + diff --git a/engine/src/flutter/lib/spirv/test/general_shaders/BUILD.gn b/engine/src/flutter/lib/spirv/test/general_shaders/BUILD.gn index 828df5de1e..01ceb01398 100644 --- a/engine/src/flutter/lib/spirv/test/general_shaders/BUILD.gn +++ b/engine/src/flutter/lib/spirv/test/general_shaders/BUILD.gn @@ -10,6 +10,8 @@ if (enable_unittests) { tool = "//flutter/lib/spirv/test:glsl_to_spirv" sources = [ + "blue_green_sampler.glsl", + "children_and_uniforms.glsl", "functions.glsl", "simple.glsl", "uniforms.glsl", diff --git a/engine/src/flutter/lib/spirv/test/general_shaders/blue_green_sampler.glsl b/engine/src/flutter/lib/spirv/test/general_shaders/blue_green_sampler.glsl new file mode 100644 index 0000000000..4d71cbb91c --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/general_shaders/blue_green_sampler.glsl @@ -0,0 +1,19 @@ +#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 oColor; + +layout ( location = 0 ) uniform sampler2D iChild; + +void main() { + // iChild1 is an image that is half blue, half green, + // so oColor should be set to vec2(0, 1, 0, 1) + oColor = texture(iChild, vec2(1, 0)); + oColor.a = 1.0; +} + diff --git a/engine/src/flutter/lib/spirv/test/general_shaders/children_and_uniforms.glsl b/engine/src/flutter/lib/spirv/test/general_shaders/children_and_uniforms.glsl new file mode 100644 index 0000000000..44928b864c --- /dev/null +++ b/engine/src/flutter/lib/spirv/test/general_shaders/children_and_uniforms.glsl @@ -0,0 +1,26 @@ +#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 color; + +layout ( location = 0 ) uniform sampler2D child1; +layout ( location = 1 ) uniform float a; +layout ( location = 2 ) uniform sampler2D child2; +layout ( location = 3 ) uniform float b; + +void main() { + // child1 is a 10x10 image where the left half is blue and the right + // half is green, and b should be 1, so c1 should be vec4(0, 1, 0, 1) + vec4 c1 = texture(child1, vec2(b, 0)); + + // child2 only contains vec4(0, 1, 0, 1). + vec4 c2 = texture(child2, vec2(0)); + + color = c1 * c2; +} + diff --git a/engine/src/flutter/lib/ui/painting.dart b/engine/src/flutter/lib/ui/painting.dart index d03efd85b4..33dcb89625 100644 --- a/engine/src/flutter/lib/ui/painting.dart +++ b/engine/src/flutter/lib/ui/painting.dart @@ -3787,16 +3787,15 @@ class FragmentProgram extends NativeFieldWrapperClass1 { ); _init(result.src, debugPrint); _uniformFloatCount = result.uniformFloatCount; + _samplerCount = result.samplerCount; } late final int _uniformFloatCount; + late final int _samplerCount; void _constructor() native 'FragmentProgram_constructor'; void _init(String sksl, bool debugPrint) native 'FragmentProgram_init'; - // TODO(chriscraws): Add `List? children` as a parameter to [build]. - // https://github.com/flutter/flutter/issues/85240 - /// Constructs a [Shader] object suitable for use by [Paint.shader] with /// the given uniforms. /// @@ -3831,24 +3830,51 @@ class FragmentProgram extends NativeFieldWrapperClass1 { /// c: [4, 5, 6] /// d: [7, 8, 9, 10] // 2x2 matrix in column-major order /// + /// `imageSamplers` must also be sized correctly, matching the number of UniformConstant + /// variables of type SampledImage specified in the SPIR-V code. + /// + /// Consider the following snippit of GLSL code. + /// + /// ``` + /// layout (location = 0) uniform sampler2D a; + /// layout (location = 1) uniform sampler2D b; + /// ``` + /// + /// After being compiled to SPIR-V `imageSamplers` must have a length + /// of 2. + /// /// Once a [Shader] is built, uniform values cannot be changed. Instead, /// [shader] must be called again with new uniform values. Shader shader({ Float32List? floatUniforms, + List? samplerUniforms, }) { if (floatUniforms == null) { floatUniforms = Float32List(_uniformFloatCount); } if (floatUniforms.length != _uniformFloatCount) { throw ArgumentError( - 'FragmentShader floatUniforms size: ${floatUniforms.length} must match given shader uniform count: $_uniformFloatCount.'); + 'floatUniforms size: ${floatUniforms.length} must match given shader uniform count: $_uniformFloatCount.'); } - final _FragmentShader shader = _FragmentShader(this, Float32List.fromList(floatUniforms)); - _shader(shader, floatUniforms); + if (_samplerCount > 0 && (samplerUniforms == null || samplerUniforms.length != _samplerCount)) { + throw ArgumentError('samplerUniforms must have length $_samplerCount'); + } + if (samplerUniforms == null) { + samplerUniforms = []; + } else { + samplerUniforms = [...samplerUniforms]; + } + final _FragmentShader shader = _FragmentShader( + this, Float32List.fromList(floatUniforms), samplerUniforms); + _shader(shader, floatUniforms, samplerUniforms); return shader; } - void _shader(_FragmentShader shader, Float32List floatUniforms) native 'FragmentProgram_shader'; + void _shader( + _FragmentShader shader, + Float32List floatUniforms, + List samplerUniforms, + ) native 'FragmentProgram_shader'; } @pragma('vm:entry-point') @@ -3857,10 +3883,15 @@ class _FragmentShader extends Shader { /// or extended directly. /// /// To create a [_FragmentShader], use a [FragmentProgram]. - _FragmentShader(this._builder, this._floatUniforms) : super._(); + _FragmentShader( + this._builder, + this._floatUniforms, + this._samplerUniforms, + ) : super._(); final FragmentProgram _builder; final Float32List _floatUniforms; + final List _samplerUniforms; @override bool operator ==(Object other) { @@ -3870,11 +3901,12 @@ class _FragmentShader extends Shader { return false; return other is _FragmentShader && other._builder == _builder - && _listEquals(other._floatUniforms, _floatUniforms); + && _listEquals(other._floatUniforms, _floatUniforms) + && _listEquals(other._samplerUniforms, _samplerUniforms); } @override - int get hashCode => hashValues(_builder, hashList(_floatUniforms)); + int get hashCode => hashValues(_builder, hashList(_floatUniforms), hashList(_samplerUniforms)); } /// Defines how a list of points is interpreted when drawing a set of triangles. diff --git a/engine/src/flutter/lib/ui/painting/fragment_program.cc b/engine/src/flutter/lib/ui/painting/fragment_program.cc index 28ab9f7b6c..b166a497d1 100644 --- a/engine/src/flutter/lib/ui/painting/fragment_program.cc +++ b/engine/src/flutter/lib/ui/painting/fragment_program.cc @@ -55,11 +55,33 @@ void FragmentProgram::init(std::string sksl, bool debugPrintSksl) { fml::RefPtr FragmentProgram::shader( Dart_Handle shader, - const tonic::Float32List& uniforms) { - auto sk_shader = runtime_effect_->makeShader( - SkData::MakeWithCopy(uniforms.data(), - uniforms.num_elements() * sizeof(float)), - 0, 0, nullptr, false); + const tonic::Float32List& uniforms, + Dart_Handle samplers) { + auto sampler_shaders = + tonic::DartConverter>::FromDart(samplers); + size_t uniform_count = uniforms.num_elements(); + size_t uniform_data_size = + (uniform_count + 2 * sampler_shaders.size()) * sizeof(float); + sk_sp uniform_data = SkData::MakeUninitialized(uniform_data_size); + // uniform_floats must only be referenced BEFORE the call to makeShader below. + auto* uniform_floats = + reinterpret_cast(uniform_data->writable_data()); + for (size_t i = 0; i < uniform_count; i++) { + uniform_floats[i] = uniforms[i]; + } + std::vector> sk_samplers(sampler_shaders.size()); + for (size_t i = 0; i < sampler_shaders.size(); i++) { + ImageShader* image_shader = sampler_shaders[i]; + // The default value for SkSamplingOptions is used because ImageShader + // uses a cached value set by the user in the Dart constructor. + // Users are instructed to make use of this in the Dart docs. + sk_samplers[i] = image_shader->shader(SkSamplingOptions()); + uniform_floats[uniform_count + 2 * i] = image_shader->width(); + uniform_floats[uniform_count + 2 * i + 1] = image_shader->height(); + } + auto sk_shader = + runtime_effect_->makeShader(std::move(uniform_data), sk_samplers.data(), + sk_samplers.size(), nullptr, false); return FragmentShader::Create(shader, std::move(sk_shader)); } diff --git a/engine/src/flutter/lib/ui/painting/fragment_program.h b/engine/src/flutter/lib/ui/painting/fragment_program.h index 16a21d4449..06cf429b32 100644 --- a/engine/src/flutter/lib/ui/painting/fragment_program.h +++ b/engine/src/flutter/lib/ui/painting/fragment_program.h @@ -31,7 +31,8 @@ class FragmentProgram : public RefCountedDartWrappable { void init(std::string sksl, bool debugPrintSksl); fml::RefPtr shader(Dart_Handle shader, - const tonic::Float32List& uniforms); + const tonic::Float32List& uniforms, + Dart_Handle samplers); static void RegisterNatives(tonic::DartLibraryNatives* natives); diff --git a/engine/src/flutter/lib/ui/painting/image_shader.cc b/engine/src/flutter/lib/ui/painting/image_shader.cc index 2b56ee08fd..60d08af12a 100644 --- a/engine/src/flutter/lib/ui/painting/image_shader.cc +++ b/engine/src/flutter/lib/ui/painting/image_shader.cc @@ -70,6 +70,14 @@ sk_sp ImageShader::shader(SkSamplingOptions sampling) { return cached_shader_.skia_object(); } +int ImageShader::width() { + return sk_image_.skia_object()->width(); +} + +int ImageShader::height() { + return sk_image_.skia_object()->height(); +} + ImageShader::ImageShader() = default; ImageShader::~ImageShader() = default; diff --git a/engine/src/flutter/lib/ui/painting/image_shader.h b/engine/src/flutter/lib/ui/painting/image_shader.h index f7d4891c39..6383f5ebce 100644 --- a/engine/src/flutter/lib/ui/painting/image_shader.h +++ b/engine/src/flutter/lib/ui/painting/image_shader.h @@ -38,6 +38,9 @@ class ImageShader : public Shader { static void RegisterNatives(tonic::DartLibraryNatives* natives); + int width(); + int height(); + private: ImageShader(); diff --git a/engine/src/flutter/lib/web_ui/lib/src/ui/painting.dart b/engine/src/flutter/lib/web_ui/lib/src/ui/painting.dart index 8082670085..1ca1b99447 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/ui/painting.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/ui/painting.dart @@ -812,6 +812,7 @@ class FragmentProgram { FragmentProgram._(); Shader shader({ - required Float32List floatUniforms, + Float32List? floatUniforms, + List? samplerUniforms, }) => throw UnsupportedError('FragmentProgram is not supported for the CanvasKit or HTML renderers.'); } diff --git a/engine/src/flutter/testing/dart/fragment_shader_test.dart b/engine/src/flutter/testing/dart/fragment_shader_test.dart index 502a033ac4..f2e6fce7a5 100644 --- a/engine/src/flutter/testing/dart/fragment_shader_test.dart +++ b/engine/src/flutter/testing/dart/fragment_shader_test.dart @@ -45,6 +45,21 @@ void main() { _expectShaderRendersGreen(shader); }); + test('blue-green image renders green', () async { + final ByteBuffer spirv = spvFile('general_shaders', 'blue_green_sampler.spv').readAsBytesSync().buffer; + final FragmentProgram program = await FragmentProgram.compile( + debugPrint: true, + spirv: spirv, + ); + final Image blueGreenImage = await _createBlueGreenImage(); + final ImageShader imageShader = ImageShader( + blueGreenImage, TileMode.clamp, TileMode.clamp, _identityMatrix); + final Shader shader = program.shader( + samplerUniforms: [imageShader], + ); + await _expectShaderRendersGreen(shader); + }); + test('shader with uniforms renders correctly', () async { final Uint8List shaderBytes = await spvFile('general_shaders', 'uniforms.spv').readAsBytes(); final FragmentProgram program = await FragmentProgram.compile(spirv: shaderBytes.buffer); @@ -236,6 +251,7 @@ Map _loadSpv(String leafFolderName) { const int _shaderImageDimension = 4; const Color _greenColor = Color(0xFF00FF00); +const Color _blueColor = Color(0xFF0000FF); // Precision for checking uniform values. const double epsilon = 0.5 / 255.0; @@ -244,3 +260,43 @@ const double epsilon = 0.5 / 255.0; double toFloat(int v) => v.toDouble() / 255.0; String toHexString(int color) => '#${color.toRadixString(16)}'; + +// 10x10 image where the left half is blue and the right half is +// green. +Future _createBlueGreenImage() async { + final int length = 10; + final int bytesPerPixel = 4; + final Uint8List pixels = Uint8List(length * length * bytesPerPixel); + int i = 0; + for (int y = 0; y < length; y++) { + for (int x = 0; x < length; x++) { + if (x < length/2) { + pixels[i+2] = 0xFF; // blue channel + } else { + pixels[i+1] = 0xFF; // green channel + } + pixels[i+3] = 0xFF; // alpha channel + i += bytesPerPixel; + } + } + final ImageDescriptor descriptor = ImageDescriptor.raw( + await ImmutableBuffer.fromUint8List(pixels), + width: length, + height: length, + pixelFormat: PixelFormat.rgba8888, + ); + final Codec codec = await descriptor.instantiateCodec(); + final FrameInfo frame = await codec.getNextFrame(); + return frame.image; +} + +// A single uniform with value 1. +final Float32List _singleUniform = Float32List.fromList([1]); + +final Float64List _identityMatrix = Float64List.fromList([ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, +]); +