Skip to content

Commit

Permalink
feat: make O'Mesh on Impeller great again (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
renancaraujo authored Aug 24, 2024
1 parent 249cfea commit 282136e
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 221 deletions.
60 changes: 34 additions & 26 deletions flutter/mesh/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
[![License: BSD-3][license_badge]][license_link]
[![pub package][pub_badge]][pub_link]

⚡ Fast and highly customizable vector animated Mesh Gradients for Flutter applications.
⚡ Fast and highly customizable animated Mesh Gradients for Flutter applications.

It is not a JPEG 🌭

<img src="https://raw.githubusercontent.com/renancaraujo/omesh/main/flutter/mesh/doc/assets/preview.png" width="500">

Expand All @@ -30,7 +32,6 @@ Try the [O'Mesh Flutter playground](https://omesh-playground.renan.gg) and gener

<img src="https://raw.githubusercontent.com/renancaraujo/omesh/main/flutter/mesh/doc/assets/playground.jpeg" width="500">


## Documentation and resources 📚

The best resources to get the most out of O'Mesh:
Expand Down Expand Up @@ -385,52 +386,59 @@ class _MyWidgetState extends State<MyWidget> with SingleTickerProviderStateMixin

## About impeller

When running on the [Impeller Rendering Engine](https://docs.flutter.dev/perf/impeller) (which is default for Flutter on iOS),
one might see some line-ish artifacts:
O'Mesh has had issues when running on the [Impeller Rendering Engine](https://docs.flutter.dev/perf/impeller) in previous versions.
These issues were caused by some instability on Impeller, specifically in the Vertices API, which O'Mesh uses in a unique way.

These issues are fixed in the latest version of O'Mesh, but if you are Opting in or out of impeller for any of the platforms, you need to consider the following:

<img src="https://raw.githubusercontent.com/renancaraujo/omesh/main/flutter/mesh/doc/assets/impeller.png" width="250">
### Impeller on iOS

This is caused by a [very specific issue](https://github.com/flutter/flutter/issues/151355) that has been already fixed in the flutter `main` channel but can still be observed on
Flutter stable version 3.22.
Impeller is [enabled by default when running Flutter on iOS](https://docs.flutter.dev/perf/impeller#ios). **If you are opting out of it**, add the following before your `runApp` call:

To work around that you can either:
```dart
enableOMeshImpellerCompatibility = false;
```

- Use a later version of flutter greater than 3.22.x, which can be in the beta or main channel. ([What?](https://github.com/flutter/flutter/blob/master/docs/releases/Flutter-build-release-channels.md)).
- Disable impeller in your project ([by following these instructions](https://docs.flutter.dev/perf/impeller#ios)).
### Impeller on macOS

If none of the above is an option for you, there is a last resource:
Set the flag `impellerCompatibilityMode` on `OMeshGradient` widget to true. This will make the mesh gradient patch fill those gaps..
Mind that this will cause different articfacts if any of the colors in the mesh semi-transparent.
Impeller is [not enabled by default on macOS](https://docs.flutter.dev/perf/impeller#macos), regardless of whether you opt-in or out of it, O'Mesh should be compatible with it.

When using `impellerCompatibilityMode`, use only opaque colors.
### Impeller on Android

Impeller is [not enabled by default on Android](https://docs.flutter.dev/perf/impeller#android). **If you are opting into it**, make sure you enabled the following flag before your `runApp` call:

```dart
enableOMeshImpellerCompatibilityOnAndroid = true;
```

## Troubleshooting

Known situations where O'Mesh doesn't seem to behave the way it is supposed to.

### OMG, there are some ugly lines on my mesh
### Panic! I am having Impeller issues

If you see lines like this:

<img src="https://raw.githubusercontent.com/renancaraujo/omesh/main/flutter/mesh/doc/assets/impeller.png" width="200">
O'Mesh had some issues when running on the [Impeller Rendering Engine](https://docs.flutter.dev/perf/impeller) in previous versions.
These issues were supposed to be fixed in the latest version of O'Mesh. If you are facing a problem that can only be observed when the impeller is ON,
report it in the Issues tab on [Github](https://github.com/renancaraujo/omesh).

It may be caused by an issue with the Impeller rendering engine. Learn the possible workarounds [here](#about-impeller).
Learn about O'Mesh support on impeller [here](#about-impeller).

### Holy smokes, my mesh animation is slooow
### Holy smokes, my mesh animation is slow

This may be caused by too many triangles in your mesh

The number of triangles in a mesh can be described by `(width-1) * (height-1) * (tessellationFactor ^ 2) * 2`.

The default tessellation factor is 12, consider lowering that value for small sized containers or for
The default tessellation factor is 12, consider lowering that value for small-sized containers or
meshes with too many vertices. See below the visual impacts of a change in the tessellation factor.

### I see some artifacts when the mesh is distorted

Visible triangles may be caused by not enough tessellation in the mesh. High tessellation factor give more
Visible triangles may be caused by not enough tessellation in the mesh. A high tessellation factor gives more
accurate curves, with a performance cost. The default tessellation factor is around 12, which should be enough
to most mesh designs.
This, of course may not be true to all mesh gradient designs and screen sizes. If you need more accurate
for most mesh designs.
This, of course, may not be true for all mesh gradient designs and screen sizes. If you need more accurate
curves, consider raising the tessellation factor, if the target devices can take the extra performance toll when
animation.

Expand All @@ -444,7 +452,7 @@ See below the differences between different tessellation factors (3 and 30):
### Issues with color space xyY

When using the xyY colorspace you may face issues when setting at least one of the vertices
to complete black. Like on the second image below:
to complete black. Like in the second image below:

<p float="left">
<img src="https://raw.githubusercontent.com/renancaraujo/omesh/main/flutter/mesh/doc/assets/xyy_issue1.png" width="200">
Expand All @@ -453,7 +461,7 @@ to complete black. Like on the second image below:

This is expected behavior, as the xyY color space relies on illumination to interpolate colors, black cannot be interpolated.

As a workaround, try to set a minimal value to at least one of the color channels (R, G or B).
As a workaround, try to set a minimal value to at least one of the color channels (R, G, or B).

In other words:

Expand All @@ -472,7 +480,7 @@ This work wouldn't be possible without some special people:
- Luke Pighetti for nerd-sniping me about this a year ago
- Lots of mathematicians behind the theories that sustain this package
- The Dart and Flutter folks at Google
- My Mom that gave birth to me
- My mom, who gave birth to me

---

Expand Down
2 changes: 2 additions & 0 deletions flutter/mesh/example/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ macos/
windows/
linux/
web/
.metadata

# Forgive me lord for I have sinned
test/

45 changes: 0 additions & 45 deletions flutter/mesh/example/.metadata

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ class _InstrinsicAnimationState extends State<InstrinsicAnimation> {
curve: Curves.easeInOut,
debugMode: DebugMode.none,
tessellation: 12,
impellerCompatibilityMode: true,
duration: const Duration(seconds: 1),
mesh: OMeshRect(
width: 3,
Expand Down
56 changes: 6 additions & 50 deletions flutter/mesh/lib/src/internal_stuff/tessellated_mesh.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ class TessellatedMesh {
required RenderedOMeshRect verticesMesh,
required RenderedOMeshRect textureMesh,
required int tessellation,
required bool impellerCompatibilityMode,
}) {
final lengthInF32 = _vertexData.length ~/ 2;
final offsetInBytes = lengthInF32 * 4;
Expand All @@ -41,15 +40,13 @@ class TessellatedMesh {
verticesMesh,
tessellation,
verticesTriangles,
impellerCompatibilityMode: impellerCompatibilityMode,
);

_writeTriangles(
cornerIndices,
textureMesh,
tessellation,
textureTriangles,
impellerCompatibilityMode: impellerCompatibilityMode,
);

return Vertices.raw(
Expand All @@ -63,54 +60,13 @@ class TessellatedMesh {
List<int> cornerIndices,
RenderedOMeshRect renderedOMeshRect,
int tessellation,
Float32List output, {
required bool impellerCompatibilityMode,
}) {
var topLeft = renderedOMeshRect.vertices[cornerIndices[0]];
var topRight = renderedOMeshRect.vertices[cornerIndices[1]];
var bottomLeft = renderedOMeshRect.vertices[cornerIndices[2]];
var bottomRight = renderedOMeshRect.vertices[cornerIndices[3]];

if (impellerCompatibilityMode) {
const displace = 2.0;
topLeft = RenderedOVertex(
p: ui.Offset(topLeft.p.dx - displace, topLeft.p.dy - displace),
north: topLeft.north,
east: topLeft.east,
south: topLeft.south,
west: topLeft.west,
);

topRight = RenderedOVertex(
p: ui.Offset(topRight.p.dx + displace, topRight.p.dy - displace),
north: topRight.north,
east: topRight.east,
south: topRight.south,
west: topRight.west,
);

bottomLeft = RenderedOVertex(
p: ui.Offset(bottomLeft.p.dx - displace, bottomLeft.p.dy + displace),
north: bottomLeft.north,
east: bottomLeft.east,
south: bottomLeft.south,
west: bottomLeft.west,
);

bottomRight = RenderedOVertex(
p: ui.Offset(bottomRight.p.dx + displace, bottomRight.p.dy + displace),
north: bottomRight.north,
east: bottomRight.east,
south: bottomRight.south,
west: bottomRight.west,
);
}

Float32List output,
) {
final surface = _BezierPatch(
topLeft: topLeft,
topRight: topRight,
bottomLeft: bottomLeft,
bottomRight: bottomRight,
topLeft: renderedOMeshRect.vertices[cornerIndices[0]],
topRight: renderedOMeshRect.vertices[cornerIndices[1]],
bottomLeft: renderedOMeshRect.vertices[cornerIndices[2]],
bottomRight: renderedOMeshRect.vertices[cornerIndices[3]],
);

var offset = 0;
Expand Down
27 changes: 0 additions & 27 deletions flutter/mesh/lib/src/widgets/omesh_gradient.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ class OMeshGradient extends StatelessWidget {
this.debugMode,
this.tessellation,
this.size,
this.impellerCompatibilityMode,
this.addRepaintBoundary,
});

Expand All @@ -90,16 +89,6 @@ class OMeshGradient extends StatelessWidget {
/// the maximum size of the parent's [BoxConstraints].
final Size? size;

/// Renders each segment of th mesh expanded 1 pixel to every direction to
/// avoid line artifacts when using the impeller backend.
///
/// This is a temporary workaround for the impeller backend,
/// and should be set to false unless using impeller is the only option.
///
/// Because every segment is expanded, this may cause other visual
/// artifacts if any of the vertices has transparent/semitransparent colors.
final bool? impellerCompatibilityMode;

/// Whether to add a [RepaintBoundary] around the mesh widget
/// to avoid repainting the whole widget tree when the mesh changes.
///
Expand All @@ -118,7 +107,6 @@ class OMeshGradient extends StatelessWidget {
debugMode: debugMode,
tessellation: tessellation ?? 12,
size: size,
impellerCompatibilityMode: impellerCompatibilityMode ?? false,
);

if (addRepaintBoundary ?? true) {
Expand Down Expand Up @@ -205,7 +193,6 @@ class AnimatedOMeshGradient extends StatelessWidget {
this.tessellation,
this.debugMode,
this.size,
this.impellerCompatibilityMode,
super.key,
});

Expand All @@ -232,16 +219,6 @@ class AnimatedOMeshGradient extends StatelessWidget {
/// the minimum size of the parent constraints.
final Size? size;

/// Renders each segment of th mesh expanded 1 pixel to every direction to
/// avoid line artifacts when using the impeller backend.
///
/// This is a temporary workaround for the impeller backend,
/// and should be set to false unless using impeller is the only option.
///
/// Because every segment is expanded, this may cause other visual
/// artifacts if any of the vertices has transparent/semitransparent colors.
final bool? impellerCompatibilityMode;

@override
Widget build(BuildContext context) {
return RepaintBoundary(
Expand All @@ -255,7 +232,6 @@ class AnimatedOMeshGradient extends StatelessWidget {
mesh: mesh,
debugMode: debugMode,
tessellation: tessellation,
impellerCompatibilityMode: impellerCompatibilityMode,
addRepaintBoundary: false,
);
},
Expand Down Expand Up @@ -369,7 +345,6 @@ class _OMeshGradient extends StatefulWidget {
required this.tessellation,
required this.debugMode,
required this.shaderProvider,
required this.impellerCompatibilityMode,
this.size,
});

Expand All @@ -378,7 +353,6 @@ class _OMeshGradient extends StatefulWidget {
final int tessellation;
final DebugMode? debugMode;
final Size? size;
final bool impellerCompatibilityMode;

@override
State<_OMeshGradient> createState() => _OMeshGradientState();
Expand All @@ -390,7 +364,6 @@ class _OMeshGradientState extends State<_OMeshGradient> {
meshRect: widget.mesh,
debugMode: widget.debugMode,
tessellation: widget.tessellation,
impellerCompatibilityMode: widget.impellerCompatibilityMode,
);

@override
Expand Down
Loading

0 comments on commit 282136e

Please sign in to comment.