diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 974f4da819..59b0e08537 100755 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -362,6 +362,8 @@ FILE: ../../../flutter/lib/ui/painting/color_filter.cc FILE: ../../../flutter/lib/ui/painting/color_filter.h FILE: ../../../flutter/lib/ui/painting/engine_layer.cc FILE: ../../../flutter/lib/ui/painting/engine_layer.h +FILE: ../../../flutter/lib/ui/painting/fragment_program.cc +FILE: ../../../flutter/lib/ui/painting/fragment_program.h FILE: ../../../flutter/lib/ui/painting/fragment_shader.cc FILE: ../../../flutter/lib/ui/painting/fragment_shader.h FILE: ../../../flutter/lib/ui/painting/gradient.cc diff --git a/engine/src/flutter/lib/ui/BUILD.gn b/engine/src/flutter/lib/ui/BUILD.gn index bd7b644a3a..fdb56caa54 100644 --- a/engine/src/flutter/lib/ui/BUILD.gn +++ b/engine/src/flutter/lib/ui/BUILD.gn @@ -30,6 +30,8 @@ source_set("ui") { "painting/color_filter.h", "painting/engine_layer.cc", "painting/engine_layer.h", + "painting/fragment_program.cc", + "painting/fragment_program.h", "painting/fragment_shader.cc", "painting/fragment_shader.h", "painting/gradient.cc", diff --git a/engine/src/flutter/lib/ui/dart_ui.cc b/engine/src/flutter/lib/ui/dart_ui.cc index 91e2c5b0d6..be820520db 100644 --- a/engine/src/flutter/lib/ui/dart_ui.cc +++ b/engine/src/flutter/lib/ui/dart_ui.cc @@ -13,7 +13,7 @@ #include "flutter/lib/ui/painting/codec.h" #include "flutter/lib/ui/painting/color_filter.h" #include "flutter/lib/ui/painting/engine_layer.h" -#include "flutter/lib/ui/painting/fragment_shader.h" +#include "flutter/lib/ui/painting/fragment_program.h" #include "flutter/lib/ui/painting/gradient.h" #include "flutter/lib/ui/painting/image.h" #include "flutter/lib/ui/painting/image_descriptor.h" @@ -67,7 +67,7 @@ void DartUI::InitForGlobal() { DartRuntimeHooks::RegisterNatives(g_natives); EngineLayer::RegisterNatives(g_natives); FontCollection::RegisterNatives(g_natives); - FragmentShader::RegisterNatives(g_natives); + FragmentProgram::RegisterNatives(g_natives); ImageDescriptor::RegisterNatives(g_natives); ImageFilter::RegisterNatives(g_natives); ImageShader::RegisterNatives(g_natives); diff --git a/engine/src/flutter/lib/ui/painting.dart b/engine/src/flutter/lib/ui/painting.dart index c5bfe51b06..408390625a 100644 --- a/engine/src/flutter/lib/ui/painting.dart +++ b/engine/src/flutter/lib/ui/painting.dart @@ -3751,108 +3751,128 @@ class ImageShader extends Shader { void _initWithImage(_Image image, int tmx, int tmy, int filterQualityIndex, Float64List matrix4) native 'ImageShader_initWithImage'; } -/// A shader (as used by [Paint.shader]) that runs provided SPIR-V code. +/// An instance of [FragmentProgram] creates [Shader] objects (as used by [Paint.shader]) that run SPIR-V code. /// /// This API is in beta and does not yet work on web. /// See https://github.com/flutter/flutter/projects/207 for roadmap. /// /// [A current specification of valid SPIR-V is here.](https://github.com/flutter/engine/blob/main/lib/spirv/README.md) /// -/// When initializing or updating the `floatUniforms`, the length of float -/// uniforms must match the total number of floats defined as uniforms in -/// the shader. They will be updated in the order that they are defined. -/// -/// For example, if there are 3 uniforms: 1 of type float, 1 type float2/vec2, -/// and 1 of type vec3/float3, and 1 mat2x2 then the length of `floatUniforms` -/// must be 10. -/// -/// The uniforms could be updated as follows: -/// -/// Consider the following snippit of GLSL code. -/// -/// ``` -/// layout (location = 0) uniform float a; -/// layout (location = 1) uniform vec2 b; -/// layout (location = 2) uniform vec3 c; -/// layout (location = 3) uniform mat2x2 d; -/// ``` -/// -/// After being compiled to SPIR-V using [shaderc](https://github.com/google/shaderc) -/// and provided to the constructor, `floatUniforms` must always have a length -/// of 10. One per float-component of each uniform. -/// -/// Dart code to update uniforms. -/// -/// `shader.update(floatUniforms: Float32List.fromList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));` -/// -/// Results of shader uniforms. -/// -/// a: 1 -/// b: [2, 3] -/// c: [4, 5, 6] -/// d: [7, 8, 9, 10] // 2x2 matrix in column-major order -/// -class FragmentShader extends Shader { +class FragmentProgram extends NativeFieldWrapperClass1 { - // TODO(chriscraws): Add `List? children` as a parameter to the - // constructor and to [update]. - // https://github.com/flutter/flutter/issues/85240 - - /// Creates a fragment shader from SPIR-V byte data as an input. + /// Creates a fragment program from SPIR-V byte data as an input. + /// + /// One instance should be created per SPIR-V input. The constructed object + /// should then be reused via the [shader] method to create [Shader] objects + /// that can be used by [Shader.paint]. /// /// [A current specification of valid SPIR-V is here.](https://github.com/flutter/engine/blob/master/lib/spirv/README.md) /// SPIR-V not meeting this specification will throw an exception. /// - /// `floatUniforms` can be passed optionally to initialize the shader's - /// uniforms. If they are not initially set, they will default - /// to 0. They can later be updated by invoking the [update] method. - /// - /// `floatUniforms` must be sized correctly, or an [ArgumentError] will - /// be thrown. See [FragmentShader] docs for details. - /// - /// The compilation of a shader gets more expensive the more complicated the source is. - /// Because of this, it is reccommended to construct a FragmentShader asynchrounously, - /// outside of a widget's `build` method, to minimize the chance of UI jank. + /// Performance of shader-compilation is platform dependent and is not + /// well-specified. Because of this, it is reccommended to construct + /// `FragmentProgram` asynchronously, outside of a widget's `build` + /// method; this will minimize the chance of UI jank. @pragma('vm:entry-point') - FragmentShader({ + FragmentProgram({ required ByteBuffer spirv, - Float32List? floatUniforms, bool debugPrint = false, - }) : super._() { + }) { _constructor(); final spv.TranspileResult result = spv.transpile( spirv, spv.TargetLanguage.sksl, ); - _uniformFloatCount = result.uniformFloatCount; _init(result.src, debugPrint); - update(floatUniforms: floatUniforms ?? Float32List(_uniformFloatCount)); + _uniformFloatCount = result.uniformFloatCount; } late final int _uniformFloatCount; - void _constructor() native 'FragmentShader_constructor'; - void _init(String sksl, bool debugPrint) native 'FragmentShader_init'; + void _constructor() native 'FragmentProgram_constructor'; + void _init(String sksl, bool debugPrint) native 'FragmentProgram_init'; - /// Updates the uniform values that are supplied to the [FragmentShader] - /// and refreshes the shader. + // 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. /// - /// `floatUniforms` must be sized correctly, or an [ArgumentError] will - /// be thrown. See [FragmentShader] docs for details. + /// This method is suitable to be called synchronously within a widget's + /// `build` method or from [CustomPainter.paint]. /// - /// This method will aquire additional fields as [FragmentShader] is - /// implemented further. - void update({ - required Float32List floatUniforms, + /// `floatUniforms` can be passed optionally to initialize the shader's + /// uniforms. If they are not set they will each default to 0. + /// + /// When initializing `floatUniforms`, the length of float uniforms must match + /// the total number of floats defined as uniforms in the shader, or an + /// [ArgumentError] will be thrown. Details are below. + /// + /// Consider the following snippit of GLSL code. + /// + /// ``` + /// layout (location = 0) uniform float a; + /// layout (location = 1) uniform vec2 b; + /// layout (location = 2) uniform vec3 c; + /// layout (location = 3) uniform mat2x2 d; + /// ``` + /// + /// When compiled to SPIR-V and provided to the constructor, `floatUniforms` + /// must have a length of 10. One per float-component of each uniform. + /// + /// `program.shader(floatUniforms: Float32List.fromList([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]));` + /// + /// The uniforms will be set as follows: + /// + /// a: 1 + /// b: [2, 3] + /// c: [4, 5, 6] + /// d: [7, 8, 9, 10] // 2x2 matrix in column-major order + /// + /// Once a [Shader] is built, uniform values cannot be changed. Instead, + /// [shader] must be called again with new uniform values. + Shader shader({ + Float32List? floatUniforms, }) { + if (floatUniforms == null) { + floatUniforms = Float32List(_uniformFloatCount); + } if (floatUniforms.length != _uniformFloatCount) { throw ArgumentError( 'FragmentShader floatUniforms size: ${floatUniforms.length} must match given shader uniform count: $_uniformFloatCount.'); } - _update(floatUniforms); + final _FragmentShader shader = _FragmentShader(this, Float32List.fromList(floatUniforms)); + _shader(shader, floatUniforms); + return shader; } - void _update(Float32List floatUniforms) native 'FragmentShader_update'; + void _shader(_FragmentShader shader, Float32List floatUniforms) native 'FragmentProgram_shader'; +} + +@pragma('vm:entry-point') +class _FragmentShader extends Shader { + /// This class is created by the engine and should not be instantiated + /// or extended directly. + /// + /// To create a [_FragmentShader], use a [FragmentProgram]. + _FragmentShader(this._builder, this._floatUniforms) : super._(); + + final FragmentProgram _builder; + final Float32List _floatUniforms; + + @override + bool operator ==(Object other) { + if (identical(this, other)) + return true; + if (other.runtimeType != runtimeType) + return false; + return other is _FragmentShader + && other._builder == _builder + && _listEquals(other._floatUniforms, _floatUniforms); + } + + @override + int get hashCode => hashValues(_builder, hashList(_floatUniforms)); } /// 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 new file mode 100644 index 0000000000..28ab9f7b6c --- /dev/null +++ b/engine/src/flutter/lib/ui/painting/fragment_program.cc @@ -0,0 +1,74 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "flutter/lib/ui/painting/fragment_program.h" + +#include "flutter/lib/ui/dart_wrapper.h" +#include "flutter/lib/ui/ui_dart_state.h" +#include "third_party/skia/include/core/SkString.h" +#include "third_party/tonic/converter/dart_converter.h" +#include "third_party/tonic/dart_args.h" +#include "third_party/tonic/dart_binding_macros.h" +#include "third_party/tonic/dart_library_natives.h" +#include "third_party/tonic/typed_data/typed_list.h" + +using tonic::ToDart; + +namespace flutter { + +static void FragmentProgram_constructor(Dart_NativeArguments args) { + DartCallConstructor(&FragmentProgram::Create, args); +} + +IMPLEMENT_WRAPPERTYPEINFO(ui, FragmentProgram); + +#define FOR_EACH_BINDING(V) \ + V(FragmentProgram, init) \ + V(FragmentProgram, shader) + +FOR_EACH_BINDING(DART_NATIVE_CALLBACK) + +void FragmentProgram::RegisterNatives(tonic::DartLibraryNatives* natives) { + natives->Register( + {{"FragmentProgram_constructor", FragmentProgram_constructor, 1, true}, + FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); +} + +void FragmentProgram::init(std::string sksl, bool debugPrintSksl) { + SkRuntimeEffect::Result result = + SkRuntimeEffect::MakeForShader(SkString(sksl)); + runtime_effect_ = result.effect; + + if (runtime_effect_ == nullptr) { + Dart_ThrowException(tonic::ToDart( + std::string("Invalid SkSL:\n") + sksl.c_str() + + std::string("\nSkSL Error:\n") + result.errorText.c_str())); + return; + } + if (debugPrintSksl) { + FML_DLOG(INFO) << std::string("debugPrintSksl:\n") + sksl.c_str(); + } +} + +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); + return FragmentShader::Create(shader, std::move(sk_shader)); +} + +fml::RefPtr FragmentProgram::Create() { + return fml::MakeRefCounted(); +} + +FragmentProgram::FragmentProgram() = default; + +FragmentProgram::~FragmentProgram() = default; + +} // namespace flutter diff --git a/engine/src/flutter/lib/ui/painting/fragment_program.h b/engine/src/flutter/lib/ui/painting/fragment_program.h new file mode 100644 index 0000000000..16a21d4449 --- /dev/null +++ b/engine/src/flutter/lib/ui/painting/fragment_program.h @@ -0,0 +1,45 @@ +// 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. + +#ifndef FLUTTER_LIB_UI_PAINTING_FRAGMENT_PROGRAM_H_ +#define FLUTTER_LIB_UI_PAINTING_FRAGMENT_PROGRAM_H_ + +#include "flutter/lib/ui/dart_wrapper.h" +#include "flutter/lib/ui/painting/fragment_shader.h" +#include "third_party/skia/include/effects/SkRuntimeEffect.h" +#include "third_party/tonic/dart_library_natives.h" +#include "third_party/tonic/typed_data/typed_list.h" + +#include +#include + +namespace tonic { +class DartLibraryNatives; +} // namespace tonic + +namespace flutter { + +class FragmentProgram : public RefCountedDartWrappable { + DEFINE_WRAPPERTYPEINFO(); + FML_FRIEND_MAKE_REF_COUNTED(FragmentProgram); + + public: + ~FragmentProgram() override; + static fml::RefPtr Create(); + + void init(std::string sksl, bool debugPrintSksl); + + fml::RefPtr shader(Dart_Handle shader, + const tonic::Float32List& uniforms); + + static void RegisterNatives(tonic::DartLibraryNatives* natives); + + private: + FragmentProgram(); + sk_sp runtime_effect_; +}; + +} // namespace flutter + +#endif // FLUTTER_LIB_UI_PAINTING_FRAGMENT_PROGRAM_H_ diff --git a/engine/src/flutter/lib/ui/painting/fragment_shader.cc b/engine/src/flutter/lib/ui/painting/fragment_shader.cc index a9a59e399b..c72460f54f 100644 --- a/engine/src/flutter/lib/ui/painting/fragment_shader.cc +++ b/engine/src/flutter/lib/ui/painting/fragment_shader.cc @@ -19,60 +19,35 @@ using tonic::ToDart; namespace flutter { -static void FragmentShader_constructor(Dart_NativeArguments args) { - DartCallConstructor(&FragmentShader::Create, args); -} - -IMPLEMENT_WRAPPERTYPEINFO(ui, FragmentShader); - -#define FOR_EACH_BINDING(V) \ - V(FragmentShader, init) \ - V(FragmentShader, update) - -FOR_EACH_BINDING(DART_NATIVE_CALLBACK) +// Since _FragmentShader is a private class, we can't use +// IMPLEMENT_WRAPPERTYPEINFO +static const tonic::DartWrapperInfo kDartWrapperInfo_ui_FragmentShader = { + "ui", + "_FragmentShader", + sizeof(FragmentShader), +}; +const tonic::DartWrapperInfo& FragmentShader::dart_wrapper_info_ = + kDartWrapperInfo_ui_FragmentShader; void FragmentShader::RegisterNatives(tonic::DartLibraryNatives* natives) { - natives->Register( - {{"FragmentShader_constructor", FragmentShader_constructor, 1, true}, - FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); + natives->Register({}); } sk_sp FragmentShader::shader(SkSamplingOptions sampling) { - // TODO(antrob): Use sampling? - // https://github.com/flutter/flutter/issues/88303 + // Sampling options are ignored, since sampling options don't make sense for + // generative shaders. return shader_; } -void FragmentShader::init(std::string sksl, bool debugPrintSksl) { - SkRuntimeEffect::Result result = - SkRuntimeEffect::MakeForShader(SkString(sksl)); - runtime_effect_ = result.effect; - - if (runtime_effect_ == nullptr) { - Dart_ThrowException(tonic::ToDart( - std::string("Invalid SkSL:\n") + sksl.c_str() + - std::string("\nSkSL Error:\n") + result.errorText.c_str())); - return; - } - if (debugPrintSksl) { - FML_DLOG(INFO) << std::string("debugPrintSksl:\n") + sksl.c_str(); - } +fml::RefPtr FragmentShader::Create(Dart_Handle dart_handle, + sk_sp shader) { + auto fragment_shader = fml::MakeRefCounted(std::move(shader)); + fragment_shader->AssociateWithDartWrapper(dart_handle); + return fragment_shader; } -// TODO(https://github.com/flutter/flutter/issues/85240): -// Add `Dart_Handle children` as a paramter. -void FragmentShader::update(const tonic::Float32List& uniforms) { - shader_ = runtime_effect_->makeShader( - SkData::MakeWithCopy(uniforms.data(), - uniforms.num_elements() * sizeof(float)), - 0, 0, nullptr, false); -} - -fml::RefPtr FragmentShader::Create() { - return fml::MakeRefCounted(); -} - -FragmentShader::FragmentShader() = default; +FragmentShader::FragmentShader(sk_sp shader) + : shader_(std::move(shader)) {} FragmentShader::~FragmentShader() = default; diff --git a/engine/src/flutter/lib/ui/painting/fragment_shader.h b/engine/src/flutter/lib/ui/painting/fragment_shader.h index 8a1a12e4e2..905711e24d 100644 --- a/engine/src/flutter/lib/ui/painting/fragment_shader.h +++ b/engine/src/flutter/lib/ui/painting/fragment_shader.h @@ -29,26 +29,16 @@ class FragmentShader : public Shader { public: ~FragmentShader() override; - static fml::RefPtr Create(); + static fml::RefPtr Create(Dart_Handle dart_handle, + sk_sp shader); sk_sp shader(SkSamplingOptions) override; - void init(std::string sksl, bool debugPrintSksl); - - void update(const tonic::Float32List& uniforms); - static void RegisterNatives(tonic::DartLibraryNatives* natives); private: - FragmentShader(); + FragmentShader(sk_sp shader); - void setShader(); - - // Since the shader source cannot be updated, the effect can be - // created once and re-used. - sk_sp runtime_effect_; - - // A new shader is created every time update is called. sk_sp shader_; }; 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 643ceb469d..ccd4cc5590 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 @@ -789,16 +789,16 @@ class ImageDescriptor { } } -class FragmentShader extends Shader { - FragmentShader({ +class FragmentProgram { + FragmentProgram({ required ByteBuffer spirv, // ignore: avoid_unused_constructor_parameters - Float32List? floatUniforms, // ignore: avoid_unused_constructor_parameters bool debugPrint = false, // ignore: avoid_unused_constructor_parameters - }) : super._() { - throw UnsupportedError('FragmentShader is not supported for the CanvasKit or HTML renderers.'); + }) { + throw UnsupportedError('FragmentProgram is not supported for the CanvasKit or HTML renderers.'); } - void update({Float32List? floatUniforms}) => - throw UnsupportedError('FragmentShader is not supported for the CanvasKit or HTML renderers.'); + Shader shader({ + required Float32List floatUniforms, + }) => 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 e9099c3420..81d4bc3a9f 100644 --- a/engine/src/flutter/testing/dart/fragment_shader_test.dart +++ b/engine/src/flutter/testing/dart/fragment_shader_test.dart @@ -16,13 +16,15 @@ import 'shader_test_file_utils.dart'; void main() { test('throws exception for invalid shader', () { final ByteBuffer invalidBytes = Uint8List.fromList([1, 2, 3, 4, 5]).buffer; - expect(() => FragmentShader(spirv: invalidBytes), throws); + expect(() => FragmentProgram(spirv: invalidBytes), throws); }); test('simple shader renders correctly', () async { final Uint8List shaderBytes = await spvFile('general_shaders', 'functions.spv').readAsBytes(); - final FragmentShader shader = FragmentShader( + final FragmentProgram program = FragmentProgram( spirv: shaderBytes.buffer, + ); + final Shader shader = program.shader( floatUniforms: Float32List.fromList([1]), ); _expectShaderRendersGreen(shader); @@ -30,18 +32,20 @@ void main() { test('shader with functions renders green', () { final ByteBuffer spirv = spvFile('general_shaders', 'functions.spv').readAsBytesSync().buffer; - final FragmentShader shader = FragmentShader( + final FragmentProgram program = FragmentProgram( spirv: spirv, + ); + final Shader shader = program.shader( floatUniforms: Float32List.fromList([1]), ); _expectShaderRendersGreen(shader); }); - test('shader with uniforms renders and updates correctly', () async { + test('shader with uniforms renders correctly', () async { final Uint8List shaderBytes = await spvFile('general_shaders', 'uniforms.spv').readAsBytes(); - final FragmentShader shader = FragmentShader(spirv: shaderBytes.buffer); + final FragmentProgram program = FragmentProgram(spirv: shaderBytes.buffer); - shader.update( + final Shader shader = program.shader( floatUniforms: Float32List.fromList([ 0.0, // iFloatUniform 0.25, // iVec2Uniform.x @@ -75,6 +79,42 @@ void main() { expect(supportedOpShaders.isNotEmpty, true); _expectShadersRenderGreen(supportedOpShaders); _expectShadersHaveOp(supportedOpShaders, false /* glsl ops */); + + test('equality depends on floatUniforms', () { + final ByteBuffer spirv = spvFile('general_shaders', 'simple.spv') + .readAsBytesSync().buffer; + final FragmentProgram program = FragmentProgram(spirv: spirv); + final Float32List ones = Float32List.fromList([1]); + final Float32List zeroes = Float32List.fromList([0]); + + { + final a = program.shader(floatUniforms: ones); + final b = program.shader(floatUniforms: ones); + expect(a, b); + expect(a.hashCode, b.hashCode); + } + + { + final a = program.shader(floatUniforms: ones); + final b = program.shader(floatUniforms: zeroes); + expect(a, notEquals(b)); + expect(a.hashCode, notEquals(b.hashCode)); + } + }); + + test('equality depends on spirv', () { + final ByteBuffer spirvA = spvFile('general_shaders', 'simple.spv') + .readAsBytesSync().buffer; + final ByteBuffer spirvB = spvFile('general_shaders', 'uniforms.spv') + .readAsBytesSync().buffer; + final FragmentProgram programA = FragmentProgram(spirv: spirvA); + final FragmentProgram programB = FragmentProgram(spirv: spirvB); + final a = programA.shader(); + final b = programB.shader(); + + expect(a, notEquals(b)); + expect(a.hashCode, notEquals(b.hashCode)); + }); } // Expect that all of the spirv shaders in this folder render green. @@ -83,8 +123,10 @@ void main() { void _expectShadersRenderGreen(Map shaders) { for (final String key in shaders.keys) { test('$key renders green', () { - final FragmentShader shader = FragmentShader( + final FragmentProgram program = FragmentProgram( spirv: shaders[key]!, + ); + final Shader shader = program.shader( floatUniforms: Float32List.fromList([1]), ); _expectShaderRendersGreen(shader); @@ -138,7 +180,7 @@ void _expectShaderHasOp(ByteBuffer spirv, String filename, bool glsl) { } // Expects that a spirv shader only outputs the color green. -Future _expectShaderRendersGreen(FragmentShader shader) async { +Future _expectShaderRendersGreen(Shader shader) async { final ByteData renderedBytes = (await _imageByteDataFromShader( shader: shader, imageDimension: _shaderImageDimension, @@ -149,7 +191,7 @@ Future _expectShaderRendersGreen(FragmentShader shader) async { } Future _imageByteDataFromShader({ - required FragmentShader shader, + required Shader shader, int imageDimension = 100, }) async { final PictureRecorder recorder = PictureRecorder();