Skip to content

Conversation

@frank-weindel
Copy link
Contributor

When enabled by the enableContextSpy renderer option, the canvas context object (WebGL in the current case) will be instrumented with a spy mechanism that counts the number of calls to each context function. The number of calls to each function during the previous FPS interval will be included in the new contextSpyData key of the 'fpsUpdate' event.

The Context Spy can be enabled in Example Tests by the contextSpy boolean URL param. When enabled, a statistical calculation of each context function call will be reported after every FPS statistics summary. Because of this, the option requires that the fps URL param is also set to true, otherwise only a degradation of performance will occur without any output of the Context Spy data.

Example output:

---------------------------------
Average FPS: 4.4
Median FPS: 4
P01 FPS: 4
P05 FPS: 4
P25 FPS: 4
Std Dev FPS: 0.48989794855663565
Num samples: 5
---------------------------------
median(disable) / median(fps): 4
median(clear) / median(fps): 1
median(bindBuffer) / median(fps): 1
median(bufferData) / median(fps): 1
median(getParameter) / median(fps): 3
median(activeTexture) / median(fps): 3
median(bindTexture) / median(fps): 3
median(uniform2fv) / median(fps): 3
median(uniform1f) / median(fps): 3
median(enable) / median(fps): 3
median(blendFunc) / median(fps): 3
median(drawElements) / median(fps): 3
median(totalCalls) / median(fps): 28
---------------------------------

BREAKING CHANGE

This is a breaking change because the 'fpsUpdate' event previously only used a number as its payload type, whereas now it is an object with the keys fps and contextSpyData. Apps/frameworks that listen for the 'fpsUpdate' event will need to update their handlers.

When enabled by the `enableContextSpy` renderer option, the canvas context
object (WebGL in the current case) will be instrumented with a spy mechanism
that counts the number of calls to each context function. The number of calls
to each function during the previous FPS interval will be included in the
new `contextSpyData` key of the 'fpsUpdate' event.

The Context Spy can be enabled in Example Tests by the `contextSpy` boolean URL param.
When enabled, a statistical calculation of each context function call will be reported
after every FPS statistics summary. Because of this, the option requires that the `fps`
URL param is also set to true, otherwise only a degradation of performance will occur
without any output of the Context Spy data.

Example output:
```
---------------------------------
Average FPS: 4.4
Median FPS: 4
P01 FPS: 4
P05 FPS: 4
P25 FPS: 4
Std Dev FPS: 0.48989794855663565
Num samples: 5
---------------------------------
median(disable) / median(fps): 4
median(clear) / median(fps): 1
median(bindBuffer) / median(fps): 1
median(bufferData) / median(fps): 1
median(getParameter) / median(fps): 3
median(activeTexture) / median(fps): 3
median(bindTexture) / median(fps): 3
median(uniform2fv) / median(fps): 3
median(uniform1f) / median(fps): 3
median(enable) / median(fps): 3
median(blendFunc) / median(fps): 3
median(drawElements) / median(fps): 3
median(totalCalls) / median(fps): 28
---------------------------------
````

BREAKING CHANGE

This is a breaking change because the 'fpsUpdate' event previously only used
a `number` as its payload type, whereas now it is an object with the keys `fps`
and `contextSpyData`. Apps/frameworks that listen for the 'fpsUpdate' event will
need to update their handlers.
@frank-weindel frank-weindel added the breaking change! This issue / PR may require downstream dependencies to make changes to retain existing functionality label Dec 20, 2023
Copy link
Contributor

@wouterlucas wouterlucas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat! This would be great base for a more advanced debugger of some kind, getting these calls/statistics is very useful.

@frank-weindel frank-weindel added this pull request to the merge queue Dec 21, 2023
Merged via the queue into main with commit 918e410 Dec 21, 2023
github-merge-queue bot pushed a commit that referenced this pull request Jan 16, 2024
**Depends on #110**
- **DO NOT MERGE** until that PR is merged to main, and be sure to set
the base to `main` after that happens.

This performance enhancement encapsulates the native WebGL context into
a wrapper which keeps track of the state of some some heavy/frequent
WebGL operations and filters out unnecessary calls to the native
context.

See description above the WebGlContextWrapper class for more information
on it.

# Performance Tests

## Chrome Desktop
The test on Chrome Desktop on my 2019 MacBook Pro showed a significant
improvement in FPS for 6000 nodes when running the
`stress-multi-texture` test. Total WebGL calls per frame were reduced by
78% and the median FPS was improved by 98%.
```
stress-multi-texture (chrome)

http://localhost:5173/?test=stress-multi-texture&overlay=false&fps=true&multiplier=60&contextSpy=true
Created 6000 nodes with alternating textures

Before:
---------------------------------
Average FPS: 30
Median FPS: 30
P01 FPS: 30
P05 FPS: 30
P25 FPS: 30
Std Dev FPS: 0
Num samples: 100
---------------------------------
median(disable) / median(fps): 6202
median(clear) / median(fps): 1
median(bindBuffer) / median(fps): 1
median(bufferData) / median(fps): 1
median(getParameter) / median(fps): 6201
median(activeTexture) / median(fps): 6201
median(bindTexture) / median(fps): 6201
median(uniform2fv) / median(fps): 6201
median(uniform1f) / median(fps): 6201
median(enable) / median(fps): 6201
median(blendFunc) / median(fps): 6201
median(drawElements) / median(fps): 6201
median(totalCalls) / median(fps): 55813
---------------------------------

After:
---------------------------------
Average FPS: 59.2
Median FPS: 59 (+96%)
P01 FPS: 58
P05 FPS: 58
P25 FPS: 58
Std Dev FPS: 0.8602325267042625
Num samples: 100
---------------------------------
median(clear) / median(fps): 1
median(bufferData) / median(fps): 1
median(bindTexture) / median(fps): 6103
median(drawElements) / median(fps): 6103
median(totalCalls) / median(fps): 12207 (-78%)
---------------------------------
```

## Raspberry Pi (RPI)
For the same `stress-multi-texture` test at 100 nodes, the Raspberry PI
B+ running WPE 2.42 showed no change in median FPS despite the total
WebGL calls per frame being reduced the same amount. This seems to stem
from the fact that the biggest bottle neck with running Lightning 3 on
the RPI is the WebGL `bufferData()` operation which is a single
operation that uploads the CPU bound Vertex Buffer to the GPU before
drawing each frame. In a performance profile from the RPI, this
operation accounts for 75% of the CPU time within 1 second of rendering,
while on Chrome Desktop the time spent is very minimal.

```
stress-multi-texture (RPI)

http://10.0.0.144:5173/?test=stress-multi-texture&overlay=false&resolution=1080&fps=true&multiplier=1
Created 100 nodes with alternating textures

Before
---------------------------------
Average FPS: 39.18
Median FPS: 45
P01 FPS: 23
P05 FPS: 24
P25 FPS: 27
Std Dev FPS: 9.643007829510463
Num samples: 100
---------------------------------
median(disable) / median(fps): 102
median(clear) / median(fps): 1
median(bindBuffer) / median(fps): 1
median(bufferData) / median(fps): 1
median(getParameter) / median(fps): 101
median(activeTexture) / median(fps): 101
median(bindTexture) / median(fps): 101
median(uniform2fv) / median(fps): 101
median(uniform1f) / median(fps): 101
median(enable) / median(fps): 101
median(blendFunc) / median(fps): 101
median(drawElements) / median(fps): 101
median(totalCalls) / median(fps): 913
---------------------------------

After
---------------------------------
Average FPS: 38.57
Median FPS: 45 (+0%)
P01 FPS: 24
P05 FPS: 24
P25 FPS: 28
Std Dev FPS: 8.771835611774769
Num samples: 100
---------------------------------
median(clear) / median(fps): 1
median(bufferData) / median(fps): 1
median(bindTexture) / median(fps): 101
median(drawElements) / median(fps): 101
median(totalCalls) / median(fps): 204 (-78%)
---------------------------------
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change! This issue / PR may require downstream dependencies to make changes to retain existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants