Jump to content

SVG to Texture off the main thread: how deep can I possibly go?


shlajin
 Share

Recommended Posts

Hey there!

I'm developing an electron app, which will load shit tons of SVG files and play them as animations. The process of decoding SVG -> PIXI.Texture is pretty fast, however, with 1000 SVGs it takes some time to process... which makes sense.
What I'd like to do is to keep the decoding outside of main thread, so user can interact with the system while images are loaded.

Since I'm using electron my first attempt was to spawn/fork a sub process and it failed dramatically, because only JSON-encoded data can be exchanged between them. Bummer.
Another option is to use electron's protocol to communicate between browser windows (IPC), which doesn't work too, since for some reason IPC can exchange even binary data, but when it comes to built-in stuff like `new Image` it fails to do so. While, theoretically, I could encode images in something else and transfer them I understand that most likely I'll lose more on added overhead... and well, I'm trying to decode less on main thread, not add something more to do so.

By searching through this forum I found few threads which discuss possibility of using service workers and, specifically, `createImageBitmap`. Since I'm using electron I can use both, and, luckily, it even supports SVG decoding! A very nice example: https://svg-zoom-demo.glitch.me/

However... I'm not quite sure what am I doing. In my perfect world, I'd use a "loading stub" as a texture for AnimatedSprite, and then, after my promise resolves, swap textures by simply changing them `animatedSprite.textures = newTextures`.

How deep can I go there? Is it possible to prepare Pixi's `Texture` to do that off the main thread? Or if I rasterize SVGs with `createImageBitmap` (and scale it accordingly altogether) I will have a huge boost already? But if I do so, will it result in X2 VRAM usage (rasterized image + a new one created by PIXI)? The latter question may sound silly, but I'm concerned about VRAM since I load a lot of images into VRAM and have no ability to check its usage...

Maybe somebody has some thoughts? Help is highly appreciated!

Link to comment
Share on other sites

The idea is to create a resource that upload asynchronously and has custom uploading logic.

https://github.com/pixijs/pixi.js/wiki/v4-Gotchas#createimagebitmap

There's pixi-heaven texture loading, there's no example for SVG, only big atlas thing, but I hope that you are good enough to change it to easy svg loading: 
https://github.com/gameofbombs/pixi-heaven/blob/master/src/hacks/BaseTexture.ts
https://github.com/gameofbombs/pixi-heaven/blob/master/src/hacks/AtlasManager.ts
https://github.com/gameofbombs/pixi-heaven/blob/master/src/superAtlas/SuperAtlas.ts#L215

It overrides pixi TextureManager updateTexture() method.

Same technology (texture resources) exists in PIXI-V5, but i dont recommend to use that branch yet.

Link to comment
Share on other sites

Yes, its still will use 2x memory like for all pixi apps, and maybe 3x somewhere. 
I didnt work on SVG specifically, but I loaded SWF files with this tech and put all vector shapes in atlases like in Mozilla Shumway project. That pixi fork will be released in christmas (probably in russian christmas)

Link to comment
Share on other sites

If you have already working subprocess that does the thing, why not pass the output as base64 data inside a json if json is the only available transfer method? That way you could just create the basetexture with image that has src as something like "data:image/png;base64, datahere".

Link to comment
Share on other sites

1 hour ago, Exca said:

f you have already working subprocess that does the thing, why not pass the output as base64 data inside a json if json is the only available transfer method? That way you could just create the basetexture with image that has src as something like "data:image/png;base64, datahere".

Resource loading happens asynchronously anyway, and since I have SVG I already use `data:xml/svg ...` as `image.src`, so I don't think I can win anything here...

 

14 hours ago, ivan.popelyshev said:

There's pixi-heaven texture loading, there's no example for SVG, only big atlas thing, but I hope that you are good enough to change it to easy svg loading: 

I've read through the SuperAtlas code and frankly, I don't understand how that can help ? either I don't get something, or I'm bad at explaining my problem... by any chance, is there any SuperAtlas usage example with regular JPG/PNG? I don't think it matters what type of image format is used, the non-blocking way of preparing images do...

Link to comment
Share on other sites

The idea is to customize your uploading process, like here:  https://github.com/pixijs/pixi.js/blob/next/packages/core/src/textures/resources/ImageResource.js#L156

However its pixi-v5, and you cant use it because there are bugs in new renderer.

pixi-heaven has the same architecture and SuperAtlas is example of usage. I'm sorry that I do not have other examples for v4. Here is snippet from another project: https://pastebin.com/LAKjnJdL , different classes, same architecture, I'm changing the way my texture loads.

If you don't understand that way, try hacking pixi TextureManager manually and then you'll get it.

Link to comment
Share on other sites

You asked the good question: how deep it can possibly go. I show you bleeding edge :)

Try porting https://github.com/pixijs/pixi.js/blob/next/packages/core/src/textures/resources/ImageResource.js on top of https://github.com/gameofbombs/pixi-heaven/blob/master/src/base/atlas/TextureResource.ts  , using superatlas as an example of interface usage.

Link to comment
Share on other sites

Yeah, I did look at it, but it feels like it may bring just some of the work behind the main thread, hence the original question.

Thank you for your help – frankly, at this point I don't have stuff setup yet, so I can't test. My idea was to start the "right way" from the scratch, but apparently there's no single silver bullet here. So, I'll continue with "silly" and straightforward way for now and then try to tune that with `createImageBitmap` and report back with results in case anyone else is wondering or encounter the same issue :) ... and, well, maybe continue discussion if results would not be as good as I aim them to be. Thanks again!

Link to comment
Share on other sites

So, I played a bit and got to this point (it'll take a bit to load images):

https://codepen.io/shlajin-the-encoder/pen/aQBvjo

I'm not sure why, but in my chrome browser's `createImageBitmap` behaves way better than inside an electron app – I can tell the difference in browser, but I cannot inside electron. Maybe it's something to do with chromium versions – chrome is 70, while latest electron has somewhat like 68/69 (it's in beta and different pieces give different info...)

Nonetheless, even in chrome I still feel a noticeable lag which I'm trying to tackle. Is it any close to the bleeding edge or I can do something else to fix it?

Link to comment
Share on other sites

I would build the images on a separate thread and write them to disk, then the main thread loads them from disk. That way next time you launch you don't need to convert them again, just load the already converted files from disk.

If possible, you can even do this at build time and ship the images in the built format.

Link to comment
Share on other sites

If possible, you can even do this at build time and ship the images in the built format.

Unfortunately, it's not possible since I'm going to re-size images a lot (from large ones to small ones) and I need to have them as crispy as I possibly can, so vector -> raster is a way to go.

I would build the images on a separate thread and write them to disk, then the main thread loads them from disk.

This sounds like a great idea to me, thanks! But at which point will it gain performance? In which format should I save them – png or there's some more "raw" format I can go with?

Link to comment
Share on other sites

You can store them as an uncompressed bitmap, but PNG is completely fine. The decode time from PNG -> bitmap is usually OK. If you want to avoid that time you can store them as dds or something similar to get some compression, but skip the decoding step that the browser would have to perform.

The performance benefit you get is just being able to skip your rasterization step. I bet that PNG -> bitmap by the browser is much faster than your SVG -> bitmap raster.

Link to comment
Share on other sites

So, save files to .dds in the other friend and pick them via main thread... am I right that I'll still have to load them via `new Image().src="my_file.dds"` or is there any other method for this?

Also, would highly appreciate if you could suggest some library to process svg -> dds :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...