mono / SkiaSharp

SkiaSharp is a cross-platform 2D graphics API for .NET platforms based on Google's Skia Graphics Library. It provides a comprehensive 2D API that can be used across mobile, server and desktop models to render images.
MIT License
4.49k stars 538 forks source link

Memory violations when generating bitmaps in background threads [BUG] #976

Open pathw0rk3r opened 5 years ago

pathw0rk3r commented 5 years ago

This is a continuation / variation on #755. I've been able to GPU generate bitmap images in background threads on all platforms, including Windows. When I monitor GPU activity when the app is running, I can see that it never goes higher than about 40%. I'd like improve the load on the GPU by generating the images on more than one background thread.

I have written a mapping utility where my app receives a collection of vector tiles which it needs to render. Each tile is independent of the others, and each tile can be very heavy: ie. contain tens of thousands of features that are drawn using paths. A typical screen contains 36 tiles and the user can interact with the screen quickly (during panning or zooming interactions) so the map must render itself very quickly. Since each tile is a unit unto itself, the ability to generate its bitmap quickly and then to weave the results onto the display is a crucial requirement. If it takes more than two seconds to update the display, the app is considered unacceptable.

I have read the Kronos EGl spec and a fair bit of documentation on OpenGL in general. My understanding is that if a GLContext is generated in an independent thread, which is associated with a PBuffer created in the same thread, and if that context is made current in that thread, then the GLContext's handle can be passed to the GrGlInterface, and a GRContext can be created from the interface so that SkiaSharp can render using the GRContext. If I follow this approach with one thread, all the needed objects (the eglDisplay, the eglConfig, the eglPbufferSurface, and eglContext generate properly with no errors. I'm able to generate bitmaps with the GRContext.

When I turn on two threads, again, all the needed object generate with no errors. The System.IntPtrs are all different, so there doesn't appear to be any overlaps. But invariably when both threads are running memory violations will occur.

Here is an example of how I create the context in UWP:

UWPGRContextProvider.txt

The mechanism is straight forward. In an independent thread I instantiate a UWPGRContextProvider object and then call its CreateGRContext method.

The method first initialises the EGL context, and then I use a custom loader to force the access to the EGL library:

image

Then I assemble the GRGlInterface using the eglContext and this custom loader. From the interface I create the GRContext.

image

If there is something I'm misunderstanding or using incorrectly, please let me know. I've tried asking the same question on the Kronos bulletin board. If you know of another place I should ask, I'm all ears!!

Thanks for any insights you can provide, Tom

pathw0rk3r commented 5 years ago

I don't know if this is of any help, but the memory exception happens on the DrawPath call. I'm not using any path effect, but there was a bug fix that went into Skia a few months back regarding making path effects thread safe (https://skia.googlesource.com/skia/+/20054dca88d578a642905b975656a4d767139d54)

pathw0rk3r commented 5 years ago

I can supply a small Visual Studio project to demonstrate how the failure occurs, if that would be of any benefit.

pathw0rk3r commented 5 years ago

TestMultithreadedGPURendering.zip