Add a samplerUniforms field for FragmentProgram (flutter/engine#29597)

This commit is contained in:
Christopher Crawford
2021-11-11 10:53:02 -05:00
committed by GitHub
parent bf71ab3987
commit 87422e6679
26 changed files with 1007 additions and 50 deletions

View File

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

View File

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

View File

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

View File

@@ -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<int, String> uniformDeclarations = <int, String>{};
/// 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<int, String> samplerSizeDeclarations = <int, String>{};
/// ID mapped to numerical types.
final Map<int, _Type> types = <int, _Type>{};
@@ -80,6 +97,9 @@ class _Transpiler {
/// ID mapped to ID. Used by [OpLoad].
final Map<int, int> alias = <int, int>{};
/// ID mapped to a string to use instead of a generated name.
final Map<int, String> nameOverloads = <int, String>{};
/// 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<bool>.filled(maxFunctionId, false)` can be used here instead.
final Set<int> visited = <int>{};
writeFunctionAndDeps(visited, entryPoint);
// Add uniform declarations to header.
if (uniformDeclarations.isNotEmpty) {
header.writeln();
final List<int> 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<int> 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());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<ImageShader>? 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<ImageShader>? 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 = <ImageShader>[];
} else {
samplerUniforms = <ImageShader>[...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<ImageShader> 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<ImageShader> _samplerUniforms;
@override
bool operator ==(Object other) {
@@ -3870,11 +3901,12 @@ class _FragmentShader extends Shader {
return false;
return other is _FragmentShader
&& other._builder == _builder
&& _listEquals<double>(other._floatUniforms, _floatUniforms);
&& _listEquals<double>(other._floatUniforms, _floatUniforms)
&& _listEquals<ImageShader>(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.

View File

@@ -55,11 +55,33 @@ void FragmentProgram::init(std::string sksl, bool debugPrintSksl) {
fml::RefPtr<FragmentShader> 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<std::vector<ImageShader*>>::FromDart(samplers);
size_t uniform_count = uniforms.num_elements();
size_t uniform_data_size =
(uniform_count + 2 * sampler_shaders.size()) * sizeof(float);
sk_sp<SkData> uniform_data = SkData::MakeUninitialized(uniform_data_size);
// uniform_floats must only be referenced BEFORE the call to makeShader below.
auto* uniform_floats =
reinterpret_cast<float*>(uniform_data->writable_data());
for (size_t i = 0; i < uniform_count; i++) {
uniform_floats[i] = uniforms[i];
}
std::vector<sk_sp<SkShader>> 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));
}

View File

@@ -31,7 +31,8 @@ class FragmentProgram : public RefCountedDartWrappable<FragmentProgram> {
void init(std::string sksl, bool debugPrintSksl);
fml::RefPtr<FragmentShader> shader(Dart_Handle shader,
const tonic::Float32List& uniforms);
const tonic::Float32List& uniforms,
Dart_Handle samplers);
static void RegisterNatives(tonic::DartLibraryNatives* natives);

View File

@@ -70,6 +70,14 @@ sk_sp<SkShader> 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;

View File

@@ -38,6 +38,9 @@ class ImageShader : public Shader {
static void RegisterNatives(tonic::DartLibraryNatives* natives);
int width();
int height();
private:
ImageShader();

View File

@@ -812,6 +812,7 @@ class FragmentProgram {
FragmentProgram._();
Shader shader({
required Float32List floatUniforms,
Float32List? floatUniforms,
List<ImageShader>? samplerUniforms,
}) => throw UnsupportedError('FragmentProgram is not supported for the CanvasKit or HTML renderers.');
}

View File

@@ -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>[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<String, ByteBuffer> _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<Image> _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(<double>[1]);
final Float64List _identityMatrix = Float64List.fromList(<double>[
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
]);