@@ -1399,9 +1399,10 @@ class Paint {
13991399 }
14001400 set shader (Shader ? value) {
14011401 assert (() {
1402- if (value is ImageShader ) {
1403- assert (! value.debugDisposed, 'Attempted to set a disposed shader to $this ' );
1404- }
1402+ assert (
1403+ value == null || ! value.debugDisposed,
1404+ 'Attempted to set a disposed shader to $this ' ,
1405+ );
14051406 return true ;
14061407 }());
14071408 _ensureObjectsInitialized ()[_kShaderIndex] = value;
@@ -3682,6 +3683,37 @@ class Shader extends NativeFieldWrapperClass1 {
36823683 /// or extended directly.
36833684 @pragma ('vm:entry-point' )
36843685 Shader ._();
3686+
3687+ bool _debugDisposed = false ;
3688+
3689+ /// Whether [dispose] has been called.
3690+ ///
3691+ /// This must only be used when asserts are enabled. Otherwise, it will throw.
3692+ bool get debugDisposed {
3693+ late bool disposed;
3694+ assert (() {
3695+ disposed = _debugDisposed;
3696+ return true ;
3697+ }());
3698+ return disposed;
3699+ }
3700+
3701+ /// Release the resources used by this object. The object is no longer usable
3702+ /// after this method is called.
3703+ ///
3704+ /// The underlying memory allocated by this object will be retained beyond
3705+ /// this call if it is still needed by another object that has not been
3706+ /// disposed. For example, a [Picture] that has not been disposed that
3707+ /// refers to an [ImageShader] may keep its underlying resources alive.
3708+ ///
3709+ /// Classes that override this method must call `super.dispose()` .
3710+ void dispose () {
3711+ assert (() {
3712+ assert (! _debugDisposed);
3713+ _debugDisposed = true ;
3714+ return true ;
3715+ }());
3716+ }
36853717}
36863718
36873719/// Defines what happens at the edge of a gradient or the sampling of a source image
@@ -4056,55 +4088,29 @@ class ImageShader extends Shader {
40564088 }
40574089 }
40584090
4091+ @override
4092+ void dispose () {
4093+ super .dispose ();
4094+ _dispose ();
4095+ }
4096+
40594097 @FfiNative < Void Function (Handle )> ('ImageShader::Create' )
40604098 external void _constructor ();
40614099
40624100 @FfiNative < Handle Function (Pointer <Void >, Pointer <Void >, Int32 , Int32 , Int32 , Handle )> ('ImageShader::initWithImage' )
40634101 external String ? _initWithImage (_Image image, int tmx, int tmy, int filterQualityIndex, Float64List matrix4);
40644102
4065- bool _debugDisposed = false ;
4066-
4067- /// Whether [dispose] has been called.
4068- ///
4069- /// This must only be used when asserts are enabled. Otherwise, it will throw.
4070- bool get debugDisposed {
4071- late bool disposed;
4072- assert (() {
4073- disposed = _debugDisposed;
4074- return true ;
4075- }());
4076- return disposed;
4077- }
4078-
4079- /// Release the resources used by this object. The object is no longer usable
4080- /// after this method is called.
4081- ///
4082- /// The underlying memory allocated by this object will be retained beyond
4083- /// this call if it is still needed by another object that has not been
4084- /// disposed. For example, an [Picture] that has not been disposed that
4085- /// refers to this [ImageShader] may keep its underlying resources alive.
4086- void dispose () {
4087- assert (() {
4088- assert (! _debugDisposed);
4089- _debugDisposed = true ;
4090- return true ;
4091- }());
4092- _dispose ();
4093- }
4094-
40954103 /// This can't be a leaf call because the native function calls Dart API
40964104 /// (Dart_SetNativeInstanceField).
40974105 @FfiNative < Void Function (Pointer <Void >)> ('ImageShader::dispose' )
40984106 external void _dispose ();
40994107}
41004108
4101- /// An instance of [FragmentProgram] creates [Shader] objects (as used by [Paint.shader] ) that run SPIR-V code.
4109+ /// An instance of [FragmentProgram] creates [Shader] objects (as used by
4110+ /// [Paint.shader] ).
41024111///
41034112/// This API is in beta and does not yet work on web.
41044113/// See https://github.com/flutter/flutter/projects/207 for roadmap.
4105- ///
4106- /// [A current specification of valid SPIR-V is here.] (https://github.com/flutter/engine/blob/main/lib/spirv/README.md)
4107- ///
41084114class FragmentProgram extends NativeFieldWrapperClass1 {
41094115 @pragma ('vm:entry-point' )
41104116 FragmentProgram ._fromAsset (String assetKey) {
@@ -4181,6 +4187,9 @@ class FragmentProgram extends NativeFieldWrapperClass1 {
41814187 @FfiNative < Handle Function (Pointer <Void >, Handle )> ('FragmentProgram::initFromAsset' )
41824188 external String _initFromAsset (String assetKey);
41834189
4190+ /// Returns a fresh instance of [FragmentShader] .
4191+ FragmentShader fragmentShader () => FragmentShader ._(this );
4192+
41844193 /// Constructs a [Shader] object suitable for use by [Paint.shader] with
41854194 /// the given uniforms.
41864195 ///
@@ -4263,6 +4272,111 @@ class FragmentProgram extends NativeFieldWrapperClass1 {
42634272 external Handle _shader (_FragmentShader shader, Float32List floatUniforms, List <ImageShader > samplerUniforms);
42644273}
42654274
4275+ /// A [Shader] generated from a [FragmentProgram] .
4276+ ///
4277+ /// Instances of this class can be obtained from the
4278+ /// [FragmentProgram.fragmentShader] method. The float uniforms list is
4279+ /// initialized to the size expected by the shader and is zero-filled. Uniforms
4280+ /// of float type can then be set by calling [setFloat] . Sampler uniforms are
4281+ /// set by calling [setSampler] .
4282+ ///
4283+ /// A [FragmentShader] can be re-used, and this is an efficient way to avoid
4284+ /// allocating and re-initializing the uniform buffer and samplers. However,
4285+ /// if two [FragmentShader] objects with different float uniforms or samplers
4286+ /// are required to exist simultaneously, they must be obtained from two
4287+ /// different calls to [FragmentProgram.fragmentShader] .
4288+ ///
4289+ /// Consider a fragment shader with uniforms like the following:
4290+ ///
4291+ /// ```glsl
4292+ /// layout (location = 0) uniform float a;
4293+ /// layout (location = 1) uniform vec2 b;
4294+ /// layout (location = 2) uniform vec3 c;
4295+ /// layout (location = 3) uniform mat2x2 d;
4296+ /// layout (location = 4) uniform sampler2d s;
4297+ /// ```
4298+ ///
4299+ /// If this shader is loaded into a [FragmentProgram] object `program` , a
4300+ /// [FragmentShader] object can be constructed as follows:
4301+ /// ```dart
4302+ /// ImageShader sampler;
4303+ /// final FragmentShader shader = program.fragmentShader()
4304+ /// ..setFloat(0, 0.0)
4305+ /// ..setFloat(1, 1.0)
4306+ /// ..setFloat(2, 2.0)
4307+ /// ..setFloat(3, 3.0)
4308+ /// ..setFloat(4, 4.0)
4309+ /// ..setFloat(5, 5.0)
4310+ /// ..setFloat(6, 6.0)
4311+ /// ..setFloat(7, 7.0)
4312+ /// ..setFloat(8, 8.8)
4313+ /// ..setFloat(9, 9.0)
4314+ /// ..setSampler(0, sampler);
4315+ ///
4316+ /// Paint paint = Paint()..shader = shader;
4317+ /// ```
4318+ ///
4319+ /// The uniforms will then be set as follows:
4320+ ///
4321+ /// a: 0.0
4322+ /// b: [1.0, 2.0]
4323+ /// c: [3.0, 4.0, 5.0]
4324+ /// d: [6.0, 7.0, 8.0, 9.0] // 2x2 matrix in column-major order
4325+ ///
4326+ /// On subsequent frames, if only the uniform `a` is required to vary, then
4327+ /// only `shader.setFloat(0, newValue)` needs to be called. The other uniforms
4328+ /// will retain their values. The sampler uniforms will also persist in the same
4329+ /// way.
4330+ class FragmentShader extends Shader {
4331+ FragmentShader ._(FragmentProgram program) : super ._() {
4332+ _floats = _constructor (
4333+ program,
4334+ program._uniformFloatCount,
4335+ program._samplerCount,
4336+ );
4337+ }
4338+
4339+ static final Float32List _kEmptyFloat32List = Float32List (0 );
4340+
4341+ late Float32List _floats;
4342+
4343+ /// Sets the float uniform at [index] to [value] .
4344+ void setFloat (int index, double value) {
4345+ assert (! debugDisposed, 'Tried to accesss uniforms on a disposed Shader: $this ' );
4346+ _floats[index] = value;
4347+ }
4348+
4349+ /// Sets the sampler uniform at [index] to [sampler] .
4350+ ///
4351+ /// All the sampler uniforms that a shader expects must be provided or the
4352+ /// results will be undefined.
4353+ void setSampler (int index, ImageShader sampler) {
4354+ assert (! debugDisposed, 'Tried to access uniforms on a disposed Shader: $this ' );
4355+ _setSampler (index, sampler);
4356+ }
4357+
4358+ /// Releases the native resources held by the [FragmentShader] .
4359+ ///
4360+ /// After this method is called, calling methods on the shader, or attaching
4361+ /// it to a [Paint] object will fail with an exception. Calling [dispose]
4362+ /// twice will also result in an exception being thrown.
4363+ @override
4364+ void dispose () {
4365+ super .dispose ();
4366+ _floats = _kEmptyFloat32List;
4367+ _dispose ();
4368+ }
4369+
4370+ @FfiNative < Handle Function (Handle , Handle , Handle , Handle )> ('ReusableFragmentShader::Create' )
4371+ external Float32List _constructor (FragmentProgram program, int floatUniforms, int samplerUniforms);
4372+
4373+ @FfiNative < Void Function (Pointer <Void >, Handle , Handle )> ('ReusableFragmentShader::SetSampler' )
4374+ external void _setSampler (int index, ImageShader sampler);
4375+
4376+ @FfiNative < Void Function (Pointer <Void >)> ('ReusableFragmentShader::Dispose' )
4377+ external void _dispose ();
4378+ }
4379+
42664380@pragma ('vm:entry-point' )
42674381class _FragmentShader extends Shader {
42684382 /// This class is created by the engine and should not be instantiated
0 commit comments