Skip to content

Drawing text is too slow #61

Description

@tzachshabtay

Prerequisites

  • I have written a descriptive issue title
  • I have verified that I am running the latest version of ImageSharp.Drawing
  • I have verified if the problem exist in both DEBUG and RELEASE mode
  • I have searched open and closed issues to ensure it has not already been reported

(Wasn't sure if this should go here or in SixLabors.Fonts)

I'm trying to convert a working game engine from system.drawing, but my game engine (which usually hits 60 FPS without a problem) shows less than 10 FPS, due to a debug label which shows the cursor position and constantly updates as the cursor moves.

When I measured the text rendering time, I saw that ImageSharp.Drawing takes somewhat between 90ms and 170ms (median: 134ms) to render the text, and system.drawing takes ~0.3ms for rendering the text, so somewhat between x300 and x400 better performance.

Here's the analysis from xamarin profiler:
Screen Shot 2020-06-25 at 11 17 54 PM

FindIntersectionWithOrientation seems to be the biggest bottleneck: I'm completely ignorant when it comes to text rendering, I'm curious why we even need to find intersections when rendering text, I always imagined a glyph is just a series of path instructions that you map to size and render as is.

Steps to Reproduce

If you want to compare to the system.drawing version, checkout the master branch, the equivalent line is here:
https://github.com/tzachshabtay/MonoAGS/blob/cadd7acbef6b1a97e47166681ba7e349074c4b47/Source/Engine/AGS.Engine.Desktop/Drawing/DesktopBitmapTextDraw.cs#L93

System Configuration

  • ImageSharp.Drawing version: 1.0.0-beta0009
  • Other ImageSharp packages and versions: ImageSharp 1.0.0-rc0003
  • Environment (Operating system, version and so on): Mac OSX Mojave 10.14
  • .NET Framework version: Mono JIT compiler version 6.4.0.208 (2019-06/07c23f2ca43 Wed Oct 2 04:52:23 EDT 2019)

Activity

  1. JimBobSquarePants commented on Jun 26, 2020

    @JimBobSquarePants
    Member

    Yeah, we've got a fair amount of performance work to do...

    There's probably a fair amount of low hanging fruit....I can take ~15% pretty easily from InternalPath.FindIntersection(in Segment source, in Segment target) by moving the method to the Segment struct which allows inlining but I think we need to do a proper review. @tocsoft is there anything you particularly would like another pair of eyes on?

  2. tocsoft commented on Jun 26, 2020

    @tocsoft
    Member

    the find intersections stuff is definitely the hottest path in the whole of our vector drawing so anything to help in there would be awesome.... other things to consider is our whole rasterization algorithm is rather naive and there might be changes we can make in there instead that could reduce the amount of can lines we perform (by default we process each pixel I think 16 times while anti-aliasing)

  3. JimBobSquarePants commented on Jun 26, 2020

    @JimBobSquarePants
    Member

    @tocsoft I've managed to take 25% off the processing time for that method so far. Having a look at some intrinsics for additional boosts.

    I have a question regarding the double precision code. My current implementation is using single precision and all the tests are passing. Is there a test I can run to prove I still require double precision?

  4. JimBobSquarePants commented on Jun 26, 2020

    @JimBobSquarePants
    Member

    @tzachshabtay I just had a quick look at your code and it looks like you are rendering the string without antialiasing in the System.Drawing version. Our default options have antialiasing enabled. Could you turn that off and then please compare performance.

  5. tzachshabtay commented on Jun 27, 2020

    @tzachshabtay
    Author

    Ah, turned off antialiasing, I'm now getting between 25-65ms (median: 45ms).
    So, great improvement, still too slow.
    FindIntersectionsWithOrientation is still the bottleneck.

  6. JimBobSquarePants commented on Jun 27, 2020

    @JimBobSquarePants
    Member

    We'll have a look but there's no guarantee that we will be able to make dramatic improvements any time soon. There's a lot to assess.

  7. antonfirsov commented on Jun 29, 2020

    @antonfirsov
    Member

    @tocsoft @JimBobSquarePants we don't need a generic intersection finding algorithm, since one of the lines is always horizontal. Additionally, the intersection finding should be a bulk operation (faster by default, even faster if SIMD optimized).

    #7 is here to deal with this, I don't think it's worth to do other optimizations before implementing that.

  8. JimBobSquarePants commented on Jun 29, 2020

    @JimBobSquarePants
    Member

    @antonfirsov The current implementation I have is based on DirectX Maths which has an SSE path I can quite easily implement if I know that I can carry on with single precision. I've not done any work adding that and won't until I can derive a test to ensure my implementation can replace the current double precision one.

    I'm really scratching the surface with the API at the moment so I wasn't aware of #7

  9. antonfirsov commented on Jun 29, 2020

    @antonfirsov
    Member

    I'm curious why we even need to find intersections when rendering text

    @tzachshabtay we need to rasterize the polygon. The current naive approach is to start by calculating line x line intersections for each horizontal pixel line crossing the polygon lines. (@tocsoft correct me if I'm wrong.) We'd be more than happy to accept a community contribution improving this.

  10. tocsoft commented on Jun 29, 2020

    @tocsoft
    Member

    That's correct except we do it multiple times per line to create a series of subpixels which we use for antialias purposes.

  11. JimBobSquarePants commented on Jun 29, 2020

    @JimBobSquarePants
    Member

    Current (25% faster) segment intersection impl.

    public Vector2 FindIntersection(in Segment target)

  12. added this to the 1.0.0 milestone on Sep 2, 2020
  13. tocsoft commented on Sep 2, 2020

    @tocsoft
    Member

    If I recall correctly I did try profiling this stuff (it was a little while ago) after this was applied and it did nicely help offset that as the point of contention.

    Code by @JimBobSquarePants seemed to work fine and help. However after implementing it the next bottleneck is the pixel blending (which lives over in the core library rather than in Drawing). I think the blender would need to be looked at next... just changing the rendering algorithm will not fix the problem we still need up to route through that blending code used by the brushes.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions