Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache API Thoughts #8

Closed
mokafolio opened this issue Jul 8, 2018 · 13 comments
Closed

Cache API Thoughts #8

mokafolio opened this issue Jul 8, 2018 · 13 comments

Comments

@mokafolio
Copy link
Owner

mokafolio commented Jul 8, 2018

In the OpenGL implementation Tarp does a lot of caching under the hood in order to minimize work. While that is good enough in most cases, @TilmannR pointed out that it could be useful to have the caching as part of the API for cases where you'd render the same path using different transformations/styles etc.. In the current version, you basically can only clone that path if you want to avoid that redrawing will invalidate the hidden cache. Here is a simple API suggestion, do you think this is good enough? Thoughts?

//this will use the current transform set on the context
tpRenderCache cache = tpCachePath(ctx, path, &style);
//this will draw the cached path data
tpDrawRenderCache(ctx, cache);
//destroy the cache
tpRenderCacheDestroy(cache);

While I think that the render cache will most likely be a struct, holding cached geometry data, exposing it to the API through a handle will allow it to potentially store GPU primitives and it just seems to be the more future proof API vs just having a struct. Any opinions on that?

Please also check out the Vulkan thoughts over here as both topics are closely related:
#7

Any thoughts welcome!

@mokafolio
Copy link
Owner Author

A crisper variant would be to simply return a new tpRenderCache handle from tpCachePath to eliminate the creation function. via. @TilmannR

@ChengCat
Copy link

ChengCat commented Oct 9, 2018

While I think that the render cache will most likely be a struct, holding cached geometry data, exposing it to the API through a handle will allow it to potentially store GPU primitives and it just seems to be the more future proof API vs just having a struct. Any opinions on that?

This depends on whether you think contents in the cache would be useful. I think I don't need access to the cache, and an opaque handle suffices.

@ChengCat
Copy link

ChengCat commented Oct 10, 2018

I had some misunderstandings. I suppose the cache would contain flattened polylines. This polyline part of cache is interesting to me, while other parts are not. I want to pass flattened polylines directly to Tarp. tpPathAddSegments would incur some overhead for this purpose, and I hope to have a new Path API (e.g. add an already flattened polyline as a contour) or pass the data to tpRenderCache directly. I think the former approach would be better, and tpRenderCache can still be an opaque handle.

The name of Segment was a bit confusing to me. I thought it means flattened polyline segments. A clearer name could be Node.

@mokafolio
Copy link
Owner Author

mokafolio commented Oct 11, 2018

Hi ChengCat,

The terminology that tarp uses is Curve for everything between segments. A Segment is basically one point and its two attached handles between two adjacent curves. That terminology is fairly common in vector graphics and I intend to keep it that way.

At no point in the API does Tarp think of paths as flattened poly lines, hence even if I'd provide another function, it would have to do the extra work/overhead under the hood. tpRenderCache won't really change much about that as far as I can tell right now, as I really don't feel like this is a common use case that the API should accommodate at the cost of implementation complexity. Hope that makes sense!

EDIT: Just as a note though, I am absolutely down to reconsider this once I get to tpRenderCache and have a better idea about what it will look like!

@ChengCat
Copy link

ChengCat commented Oct 14, 2018

Hi, I have just looked into the terminology issue.

I have used several vector graphics libraries, and can't recall any previous confusion of 'segment'. Cairo seems to use the 'segment' as straight line segments, and nanovg uses 'segment' in documentation as the first way of using segment in SVG specification (see below).

I have also looked into the SVG 1.1 specification, and it seems to be inconsistent with what segment is.

  • In section 8.3.5 of https://www.w3.org/TR/SVG11/paths.html,

    A cubic Bézier segment is defined by a start point, an end point, and two control points.

  • and then, in section 8.5, the definitions of various SVGPathSegs are essentially the corresponding path commands.

If the the goal is to be consistent with the definitions of SVGPathSegs, I think you have done it wrong. Each segment should contain a curve-to path command, i.e. the destination point and two control points. The current Segment is defined similarly to the path nodes in vector graphics editing software, so I suggest again Node is better.

Please point out any mistakes that I may have made.

@ChengCat
Copy link

ChengCat commented Oct 14, 2018

Regarding the issue of passing flattened poly lines to Tarp. I think that is essentially a performance optimization thing. Tarp currently provides an elegant API of the usual vector graphics features, and I appreciate it. With the usual vector graphics features, I think flattened poly lines is usually expressed by consecutive line-to's.

I can totally understand that you don't want to make the API any less elegant. But I think I should let you know about this potential use case. Consecutive line-to's are needed, at least when (1) plotting a graph of an arbitrary mathematical function, (2) the path is procedurally generated, and the result is represented as poly lines for simplicity.

@mokafolio
Copy link
Owner Author

Hi ChengCat,

Regarding naming. I think my initial response to that was not clear enough. I am maintaining a C++ port of PaperJS (http://paperjs.org/) which a lot of the rendering that tarp is using originated from. So the terminology of segments and curves is based on that and I intend to stick with it. A segment is basically what is considered an Anchor point in most design software (i.e. Adobe Illustrator) while a curve is the actual curve between two of those segments. Hope that makes sense!

Regarding the poly lines: Tarp does not keep track of the actual drawing commands at all but simply builds lists of segments and curves (see above). This makes the implementation very clean in my opinion as all cases are treated the same, as I don't need to keep track of any commands or even know how a certain path was created (it still optimizes for line segments during path flattening though). So the main issues is actually not from an API perspective but implementation complexity as adding what you are suggesting would require a major rework of that logic.

What I'd be down to provide is a function to simply load/add a list of Vec2 as a polyline and add the missing information under the hood if your concern is usability rather than optimization for now!

@ChengCat
Copy link

ChengCat commented Oct 16, 2018

What I'd be down to provide is a function to simply load/add a list of Vec2 as a polyline and add the missing information under the hood if your concern is usability rather than optimization for now!

There seems to be some misunderstandings. As I have said in the previous comment, polyline is already possible via consecutive line-to's, and I think this is the standard way to do polylines in vector graphics. The only problem is how to get this as fast as possible in a real time environment. No special API for polyline is needed, if it's possible to get optimal performance without one.

I have read some of Tarp's source code, and can understand the complexity involved under-hood. I can totally understand that you may not want to tackle this problem at this time. I am going to use your library nonetheless, and when the need for better performance arises, I would customize Tarp for a bit. I think the easiest way to do this is (1) add a new set of polyline contours in _tpGLPath, and (2) make _tpGLStroke work with a NULL _joints. This is a little ugly, and you may not want to do this in your library. The performance improvement needs to be benchmarked, and I would definitely delay this until I find it really necessary.

@mokafolio
Copy link
Owner Author

mokafolio commented Oct 16, 2018

If you want to draw the strokes without joints anyways and really want to go fast, I'd just draw the line segments directly with openGL. The only thing you'd loose in terms of functionality is gradients!

@ChengCat
Copy link

By "make _tpGLStroke work with a NULL _joints", I mean the _joints array do not need to be allocated, and thus remove one overhead. The stroke poly line needs to be offsetted with respect to the stroke width before passing to OpenGL, and bevel joints are still needed during that offsetting.

The only part of Tarp that I am considering to work around is path flattening.

@mokafolio
Copy link
Owner Author

mokafolio commented Oct 17, 2018

Ah sorry, that makes sense! I think in that case we could most likely provide an API function that does that. I like that idea! I think introducing some struct to the API that encapsulates the flattened path data (that you in turn can build yourself) might be a clean solution. My gut feeling is that it should be separate from the render cache though, as that might also cache some backend specific things. Let me know if you start on this, curious to see what you come up with!

@mokafolio
Copy link
Owner Author

mokafolio commented Oct 18, 2018

Lets continue the discussion about the poly line / flattened path feature over here: #11

@mokafolio mokafolio reopened this Nov 1, 2018
@mokafolio
Copy link
Owner Author

Initial version + example of cache render API can be found in version 0.1.3 (62f5287). Closed for now, open new issues related to specific bugs when they come up!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants