redox-os / orbtk

The Rust UI-Toolkit.
MIT License
3.78k stars 188 forks source link

Consider using tiny-skia #357

Closed RazrFalcon closed 2 years ago

RazrFalcon commented 4 years ago

Since you're already using raqote, I though you might be interest in a faster, more actively developed alternative.

tiny-skia is a direct raqote rival. It's basically a Skia CPU subset ported to pure Rust. And it's usually 2-6x faster than raqote, while still 2-3 times slower than Skia (it's mainly SIMD issues). In many cases it's even faster than cairo.

Is still in development (90% is done) and I plan to use it in resvg very soon. And I'm interested in any kind of feedback.

FloVanGH commented 4 years ago

hi, thank you nice work 🙂. Looks promising I will try to create a tiny-skia render backend.

arctic-alpaca commented 4 years ago

tiny-skia is looking great. Would this mean no text on Canvas widgets? As far as I can tell, raqote does support (some) text while tiny-skia doesn't/won't for quite some time support any.

FloVanGH commented 4 years ago

@arctic-alpaca with the text drawing should be not problem, we uses for raqote also a custom implementation based on rusttype.

RazrFalcon commented 4 years ago

@arctic-alpaca raqote has a very rudimentary text support which relies on system libraries. I plan to use a pure Rust implementation eventually.

arctic-alpaca commented 4 years ago

Thanks for the explanations! @FloVanGH Out of interest and to understand OrbTk better, what needs to be done to implement a new drawing backend? Is it enough to implement RenderContext2D and modify the renderer/src/lib.rs file?

FloVanGH commented 4 years ago

@arctic-alpaca you are welcome. Yes that is the main effort of implement a new render backend. What you have to do then is to connect you RenderContext2D with a window. That is done inside of the shell crate.

But with alpha-5 I want to decouple that parts more from the other crates. My goal is that you can inject render context and events from outside. This will make it possible to use OrbTk also I use of a game engine.

kivimango commented 4 years ago

maybe it is worth to creating a RenderBackend trait.

FloVanGH commented 4 years ago

@kivimango yes that's the plan 🙂. And I want to separate each render and window backend in a own crate. This will reduce a lot of cfg code.

FloVanGH commented 4 years ago

With the upcoming milestone alpha5 I want to do same further performance improvements. And I will also start an experimental tiny-skia render backend.

RazrFalcon commented 4 years ago

Glad to here. After 3 weeks, I'm still at 90%. This is just how software development works. =)

The library become 20-50% faster and there are only two things left: stroke dashing (easy) and clipping (hard). Once again, I hope to finish in two weeks. But we'll see.

From the performance prospective, tiny-skia is 2-3x faster than cairo in most cases, but there are still weird cases when cairo is faster. And compared to raqote, we are 4-7x faster, depending on a task. Also, unlike raqote, tiny-skia supports bicubic bitmaps transformation and hairline stroking (stroke-width < 1px).

FloVanGH commented 4 years ago

Glad to here. After 3 weeks, I'm still at 90%. This is just how software development works. =)

That's true ;-)

The library become 20-50% faster and there are only two things left: stroke dashing (easy) and clipping (hard). Once again, I hope to finish in two weeks. But we'll see.

That's great I think clippling is the last part we currently need from tiny skia.

sandmor commented 4 years ago

Here is an implementation of the tiny-skia backend, currently is not complete but is understandable for the majority of the widgets, missing functionalities needed to finish it:

Also, I have a problem with the gradients, for some reason they don't work, and LinearGradient::new returns me None

RazrFalcon commented 4 years ago

Yes, clipping with path will be supported in the release.

Support for draw arcs, with beziers you only can approximate them but never match it!.

Skia converts Arcs to conic curves and then into quads. So it still not as perfect, I guess. tiny-skia skips arcs completely for now.

LinearGradient::new returns me None

Can you provide the input values you are using?

sandmor commented 4 years ago

Can you provide the input values you are using?

I think is better with the context, I checked the input in which it fails is not empty and its two points are separated, also it fails in points_to_unit_ts but I do not understand that function!

Yes, clipping with path will be supported in the release.

I saw the roadmap thank you!

tiny-skia skips arcs completely for now

Well, then I will implement them with beziers, thanks Tell me if I'm wrong: for a gradient, the coordinates to pass are the ones of the two ends, right?

RazrFalcon commented 4 years ago

Yes, I saw this code. I'm asking about the actual values/number, so I can try to reproduce it myself.

points_to_unit_ts

Looks like a malformed transform for me.

sandmor commented 4 years ago

Sorry, here is the input values for one of the gradients of the calculator example:

[crates/render/src/tiny-skia/mod.rs:89] tstart = Point {
    x: 132.0,
    y: 332.0,
}
[crates/render/src/tiny-skia/mod.rs:89] tend = Point {
    x: 132.0,
    y: 284.0,
}
[crates/render/src/tiny-skia/mod.rs:89] &g_stops = [
    GradientStop {
        position: 0,
        color: Color {
            r: 0.47843137,
            g: 0.56078434,
            b: 0.6392157,
            a: 1,
        },
    },
    GradientStop {
        position: 1,
        color: Color {
            r: 0.39215687,
            g: 0.48235294,
            b: 0.5686275,
            a: 1,
        },
    },
]
[crates/render/src/tiny-skia/mod.rs:89] spread = Pad

Looks like a malformed transform for me.

I'm using Transform::identity() directly in the argument

RazrFalcon commented 4 years ago

Thanks. I was able to reproduce it. Will see what's wrong.

PS: this is because start_x and end_x are the same. Not sure why this is a problem.

RazrFalcon commented 4 years ago

It wasn't a trivial fix, but it's fixed now.

sandmor commented 4 years ago

Thank you very much, your crate is pretty fast

RazrFalcon commented 4 years ago

Does it visually noticeable compared to raqote?

sandmor commented 4 years ago

Very, I don't know if I'm oversensitive but for me is a great speed up. You can check yourself. Also, can you give a manner to modifying the alpha channel of a Color after its creation, please? I need to apply for the global alpha.

RazrFalcon commented 4 years ago

Nice to hear.

I will add set_* methods to Color.

RazrFalcon commented 4 years ago

@sandmor I've added set_* methods as well as Color::apply_opacity, which should be handy.

sandmor commented 4 years ago

Thanks, but you don't have to run so much :smile:, this of anyway will not get merged until at least rectangular clipping gets supported.

RazrFalcon commented 4 years ago

@sandmor I've just added clipping.

sandmor commented 4 years ago

@RazrFalcon Thank you, currently I'm working in the arcs, draw the extremes is a bit complex but with the help of Stack Overflow I expect to soon have the function ended then I will look into the path and if there is another bugs on my implementation, if not I will do a pull request, Thanks!

sandmor commented 4 years ago

@RazrFalcon is set_clip_path stackable? so, I can cut a region, build another path and stack it over, so the two ones clip the following operations? that is not require for the UI but so is as RenderContext2d would work... well, that is a corner case(I think) so don't worry very much about. Also, can you setup a boundings check when a path is traced? I hit various "unreachable" errors in my experiments when I attempt to draw off-boundings, that is very annoying when you just want to see why your operation is doing that, also I think that can be trigger by OrbTk itself in some cases attempting to draw the UI, so can you fix that, please? EDIT: OrbTk uses save and restore methods to deal with multiple paths if you are interested although I will do a PR of anyway at least as an experimental backend

RazrFalcon commented 4 years ago

@sandmor I can port Skia's implementation of arc if you like. It's very robust.

No, tiny-skia is intentionally doesn't have "layers". Maybe it would change someday, but for now is just a single layer.

I hit various "unreachable" errors in my experiments when I attempt to draw off-boundings

This must not happen. Could you provide a minimal example?

OrbTk uses save and restore methods to deal with multiple paths

What paths? Clip paths?

sandmor commented 4 years ago

This must not happen. Could you provide a minimal example? Is weird I can not reproduce it out of my code and the thing I was used to test was one of my recently started projects that I modify to use as canvas to draw arcs, I'd prefer not show it although I trigger one the errors here, apparently you have a bunch of unreachable functions that are called. No, tiny-skia is intentionally doesn't have "layers". Maybe it would change someday, but for now is just a single layer. I don't need layers and OrbTk don't use it neither, but works as next:

  • You have a not clipped canvas
  • You build a path
  • You save the current canvas state
  • You use the path to clip the desire area
  • You draw some stuff
  • You build another path
  • You use the new path to clip the desire area, this results in a clipped area that is the overlaps of the already apply paths
  • Then you can call restore and restore the clipped area to the last save state. For example, I apply a square mask using clipping, then I clip another time but with a circular mask, I expect the next result if I paint the whole area: canvas Like you see, I don't need multiple layers, only the capability of save the clipping mask when RenderContext2D.save is called and the capability of overlap the new clipping path(or clipping mask) with the previous one. I can use set_clip_mask or something then to restore the saved clipping mask. I can port Skia's implementation of arc if you like

Don't worry I already implemented it, although I'd appreciate if you can view some obvious optimization on my code.... I think it have too many conditions I tried to decrease them but I don't have many experience with this.

RazrFalcon commented 4 years ago

Yes, tiny-skia doesn't support save/restore either. At this point I guess it would be easier to re-implement in on the orbtk side. So you will need to store the last/current transform and clip mask. And I can make the clip mask public. I'm trying too keep tiny-skia as simple as possible.

This is the one I'm using in resvg. It works fine. Skia uses a different algorithm. And don't worry about performance. Compared to actual rendering it's nothing.

I trigger one the errors here

It was already fixed. At least on my tests.

sandmor commented 4 years ago

it would be easier to re-implement in on the orbtk side

Yeah, good idea, but then I need some tools: The capability of get and set the clippping mask, and the capability to overlap a path area over a clipping mask or if you can maintain simple the things at cost of some performance then only the power of generate a clipping mask from a path and then I manually merge the result with the current clipping mask

This is the one I'm using in resvg. It works fine.

So... I can not see how it draws an arc, only calculates some parameters, I can do that with sin/cos, the problem is calculate the bezier control points needed to trace the arc contour but don't worry you have reason, the performance is negligible in this case I only like to make the things as efficiently as possible... even if somethings sacrifice some code prettiness

rzerres commented 2 years ago

Merged in.