You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This PR introduces the 'webgl' renderer which is basically canvas but much lower level and much faster. I see this eventually replacing the canvas renderer but it will need some time before that happens.
But there's already a canvas renderer?
To illustrate the difference when drawing, the canvas renderer basically does this:
Tell the GPU to draw character 0,0 from texture a
Tell the GPU to draw character 1,0 from texture a
Tell the GPU to draw character 2,0 from texture a
and so on.
These individual instructions are costly, the webgl renderer instead builds a Float32Array containing all the data needed to draw and a "webgl program" (a vertex shader and a fragment shader) that knows how to draw things from the Float32Array is uploaded to the GPU which does the actual drawing. Of course the details are much more involved than this, but that's the basics of why the webgl renderer will be superior to the canvas renderer.
Features
It's super fast, it also scales much better with really large viewports.
All characters are cached in the atlas, including unicode, combined chars (emoji), etc.
The atlas trims glyphs to their minimal rectangles and stores that in the texture, this means better space utilization of the texture. Here's a smaller 256x256 example illustrating this:
Renderer pausing when the canvas is offscreen is supported
Notes
WebGL2 is used, this reduces browser compatibility a little but allows using some niceties like vertex array objects and drawing using drawElementsInstanced.
Render layers still work, the webgl renderer actually still utilizes the CursorRenderLayer and LinkRenderLayer in order to simplify the rendering logic (and because they're pretty fast anyway).
When the texture atlas has reached its capacity, it will currently just clear itself and restart. This could be improved but this route was chosen due to the relatively inexpensive cost of starting over and so we don't need to juggle multiple textures.
I experimented with disabling double buffering and drawing the minimal set of changes, it didn't appear to be worth the effort (at least given the time I had to work on this).
Issues
π= merge blocker
Currently you cannot customize the selection color. This is because cached glyphs do not currently support true color, this should be trivial to fix once true color is added.
π Character joiners are not supported yet.
π allowTransparency is not supported yet.
Rendering seems to become offset on my macbook when rows are increased after 300x91, I'm guessing this is related to maximum canvas dimensions?
Comparing to canvas renderer
Summary
This table shows the average time to draw the frame before and after, see below for more details.
Benchmark
Canvas/dynamic (avg ms/frame)
WebGL (avg ms/frame)
Change (c/w-1)
Macbook 87x26
4.80
0.69
596% faster
Macbook 300x80
15.28
3.69
314% faster
Windows 87x26
7.31
0.73
901% faster
Windows 300x80
19.34
2.06
839% faster
Macbook 87x26 CJK
14.63
5.93
147% faster
Macbook 87x26 Emoji
27.47
19.28
42% faster
Methodology
The tests are done by running various commands in the build xterm.js repo directory and the following measuring code is injected to get the report:
(<any>window).frames=[];(<any>window).report=()=>{constframes=(<any>window).framesasnumber[];constaverage=Math.round(frames.reduce((p,c)=>p+c,0)/frames.length*100)/100;console.log(`...: frames ${frames.length}, average ${average}ms`);};
Wrapping _renderRows of Renderer.ts (canvas) and WebglRenderer.ts:
Parts of the chrome timelines are also presented. Note that the injected code is necessary because it's difficult to measure otherwise as the number of frames and average frame time isn't captured by the timeline.
The Macbook Pro is mid 2014 with an Intel Iris Pro 1536 MB, the Windows box has a GTX 760.
@Tyriar Have not yet looked at the code but could not resist to do a few tests on my own. Therefore first a few notes about the perf results.
I see about the same perf boost (rendering being 3-5x faster) on Linux with either intel graphics or nvidia GT520M (very old) and GTX 960. With my typical ls -lR /usr/lib benchmark I also hit the system throughput, means the terminal has to wait in between for the pty to deliver more data, and is idle the rest of the time (both systems with SSDs lol). That are really great results!
Your timelines show weak performance for windows systems, really slow incoming data, hmm. This might be worth to be addressed upstream in win-pty/conpty.
Your results also show a big perf decrease on the MacBook for the bigger terminal view. I wonder how fullscreen would perform. Note that I cant resemble this decrease with Linux (even tried 500x100), I see only slightly worse runtime for intel or nvidia gc. Maybe thats a resolution problem (retina?) and could be tweaked further?
What I see now for my benchmark in the demo:
total runtime: ~1800 ms (seems this cannot go faster due to system limits)
~ 120 ms render time (was 500 ms before) π
~ 900 ms for the input chain (parser, buffer inserts, state handling) π
~ 200 ms JS overhead (function calls, GC etc.)
~ 600 ms idle π
Last but not least: since many peeps nowadays are on laptops - does anyone know if the gl renderer would drain more power than the canvas renderer? Note that the canvas renderer also relies on hardware acc, still unsure whether this might trigger a more costly power profile.
On support, according to https://webglstats.com/ 68% of desktops support WebGL2. I looked into downgrading to WebGL1 (98% support) but there doesn't appear to be an extension for layout. I'm thinking we should push forward with replacing the canvas renderer with the WebGL2 implementation once stable and improve the DOM renderer a little so it has more reasonable performance, that way the majority of consumers will be on the best implementation, we don't muck the code up with WebGL1 messiness (+slowness?) and there is a viable alternative for the rest.
@Tyriar Yeah seems only Safari cant do Webgl2, I am not sure if Safari will ever see this as Apple is pushing WebGPU based on Metal/Vulkan. Webgl1/2 is based on OpenGL which got deprecated on macOS.
Currently experimenting with minimizing the amount of data uploaded to the GPU via bufferData, results looks favorable so far, especially when scrolling.
Move CharData usage over to CellData and remove CharDataCompat.ts
WebglAddon.dispose is not implemented
However it's a standalone addon now, the only thing that affects the core is the very small diff outside of the addons/ directory. Once merged in we'll get the build and tests in CI to prevent future breakages and we can create issues to finish off the above issues.
I've created a bunch of issues tracking the remaining issues under the addon/webgl label. Community welcome to help finish it off π. If I missed something please create another issue.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR introduces the
'webgl'
renderer which is basically canvas but much lower level and much faster. I see this eventually replacing the canvas renderer but it will need some time before that happens.But there's already a canvas renderer?
To illustrate the difference when drawing, the canvas renderer basically does this:
0,0
from texturea
1,0
from texturea
2,0
from texturea
These individual instructions are costly, the webgl renderer instead builds a
Float32Array
containing all the data needed to draw and a "webgl program" (a vertex shader and a fragment shader) that knows how to draw things from theFloat32Array
is uploaded to the GPU which does the actual drawing. Of course the details are much more involved than this, but that's the basics of why the webgl renderer will be superior to the canvas renderer.Features
Notes
drawElementsInstanced
.CursorRenderLayer
andLinkRenderLayer
in order to simplify the rendering logic (and because they're pretty fast anyway).Issues
π= merge blocker
allowTransparency
is not supported yet.Comparing to canvas renderer
Summary
This table shows the average time to draw the frame before and after, see below for more details.
Methodology
Wrapping
_renderRows
of Renderer.ts (canvas) and WebglRenderer.ts:Parts of the chrome timelines are also presented. Note that the injected code is necessary because it's difficult to measure otherwise as the number of frames and average frame time isn't captured by the timeline.
The Macbook Pro is mid 2014 with an Intel Iris Pro 1536 MB, the Windows box has a GTX 760.
Macbook Viewport 87x26 (demo default),
ls -lR .
Canvas Renderer (dynamic atlas)
WebGL Renderer
Macbook Viewport 300x80,
ls -lR .
Canvas Renderer (dynamic atlas)
WebGL Renderer
Windows Viewport 87x26 (demo default),
tree
Canvas Renderer (dynamic atlas)
WebGL Renderer
Windows Viewport 300x80,
tree
Canvas Renderer (dynamic atlas)
WebGL Renderer
Macbook Viewport 87x26,
cat zh_wcag_100.txt
zh_wcag_100.txt contains this contents of https://www.w3.org/Translations/WCAG20-zh/ 100 times to help test throughput for wide characters.
Canvas Renderer (dynamic atlas)
WebGL Renderer
Macbook Viewport 87x26,
cat emoji_100.txt
emoji_100.txt contains this contents of https://getemoji.com/ times to help test throughput for combined characters.
Canvas Renderer (dynamic atlas)
WebGL Renderer
Fixes #720
Fixes #1138 (microsoft/vscode#35901)
Fixes #1170 (I think)
Obsoletes a bunch of issues related to the canvas renderer: #941, #955, #1389, #1614