6

(New to OpenGL / Emscripten)

For a stock trading client application I'm building there is the need of 40+ open charts. About 50% of them being in some sort of 'Free draw' state, meaning they show candlesticks plus other lines/arrows/images etc.

After trying a lot of options the last few months, here is what it comes down to.

  • HighCharts: Easy but slow;
  • CanvasJS: Faster but not fast enough
  • WebAssembly + OpenGL: A lot faster, but a lot of work (still worth it)

I bootstrap a single WebAssembly app instance, and call functions on it to let C++ create charts with OpenGL, that maps to WebGL(2). It all works fine.

The reason I go for (WebAssembly + OpenGL) -> Emscripten, is because there is a lot of number crunching, and c++ suits that job fine as well :)

Problem is WebGL has a context limit of about 10 in Chrome(59). So having 40-100 WebGL context (Charts) is not a smart idea, also my guts tell me its a waste of OpenGL resources to have so many context that are almost always output as static images, unless you scroll the chart etc.

Does anyone have good experience with rendering a single OpenGL context to a random canvas element (or any other element, doesn't really matter)?

My thought are as follow:

  1. Start c++ OpenGL with an offscreen canvas in another thread, https://github.com/OleksandrChekhovskyi/emscripten-offscreen-canvas-test/blob/master/main.c#L35
  2. Javascript tells c++ to render a graph
  3. share/render the OpenGL backbuffer with JS through shared Uint8Array... SharedArrayBuffers gets filled by C++ in JS Worker thread and the main (render) thread only reads/transpiles to write image to canvas/html element.

I can't seem of any other way to not create many OpenGL contexts.

Question is: How performant will it be to do it like this, and basically copy over the OpenGL buffer to Javascript etc? It it far off track?

Thanks

enter image description here

p.s. bottom graphs (with red wave line) are now rendered by WebAssembly and OpenGL (GLFW etc)

------ UPDATE -----

Option 2: Always render to same Canvas, and use JS to copy context of canvas to another canvas (but it will probably be erased if the context updates..)

DutchKevv
  • 1,659
  • 2
  • 22
  • 39
  • "*the need of 40+ open charts*" Is there? If you're not actually displaying that many all at once, then you don't really need to have that many separate contexts. You only need to have enough contexts for what is being displayed, not for what might be displayed. – Nicol Bolas Jul 15 '17 at 18:00
  • They basically are all rendered and randomly updating, but its not inside a single container.. There can be a modal with a chart (or 3), a side slider with a chart and inside debugger etc.. If it was a full Rendered OpenGL App I would go for a single context, but mixed within HTML it can get harder.. Also I don't want to re-render all Charts if only one changes – DutchKevv Jul 15 '17 at 18:03

1 Answers1

5

So, after building some more I found a fast solution.

Im using just 1 context (GLFW) and trigger a C++ function through JS to render the chart, once done c++ signals back to JS using EM_ASM_ with the corresponding Chart ID to render the result (image) to its destination canvas width:

chart.el.getContext('2d').drawImage(Module.canvas, 0, 0, width, height, 0, 0, width, height);

I tested it and it works really fast, copying over the image from the canvas is always less than 1ms. Even on a 4K screen with 2K+ images.

And when Chrome fully supports off-screen canvas, I can load the WebAssembly rendering part in a web worker and bypass the main thread in total, and maybe switch canvas contexts on the fly to render to the destination canvas instantly. making it even faster. But its not possible yet (few more months :))

So unless Browsers will support 100+ webGL contexts, this will be the fastest solution in the nearby future I guess.

DutchKevv
  • 1,659
  • 2
  • 22
  • 39
  • Very interesting. To clarify, are you rendering a chart in C++/GLFW, & sending the resultant image back to Javascript, where it gets blitted onto a portion of the canvas? Might you provide example code for this? Just context creation, without any of the meat of the financial app? – Jack Feb 09 '18 at 16:31
  • 1
    Hi there @Jackalope.. Didn't touch the project for a while. But to clarify a bit: A hidden canvas element as only GLFW context. Every DOM canvas creates C++ class instance with unique ID, every time visible canvas needs re-render (e.a. by clicking etc), call C++ method through WASM func with unique canvas ID and changed params (w/h), call draw func on corresponding c++ class and let GLFW render to main context, c++ signals back to JS that render is complete with unique ID, JS converts hidden canvas to BPM image, JS inject image into canvas that has corresponding #id. All in parallel :) – DutchKevv Feb 16 '18 at 01:40
  • @DutchKevv can you share the relevant code that does this? – ZivS Nov 19 '18 at 09:05
  • The fastest solution is to use viewport/scissor and one context the size of the screen. It avoids all the copies. you can see an example of the solution [here](https://stackoverflow.com/a/30546250/128511) – gman Mar 10 '19 at 18:13