mirror of
https://github.com/status-im/whispervis.git
synced 2025-02-12 21:16:27 +00:00
Add render throttling to decrease CPU/GPU usage while screen is idle
This commit is contained in:
parent
19ed6ded1e
commit
b50c31811d
31
animate.go
31
animate.go
@ -5,11 +5,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gopherjs/gopherjs/js"
|
"github.com/gopherjs/gopherjs/js"
|
||||||
|
"github.com/gopherjs/vecty"
|
||||||
"github.com/status-im/simulation/propagation"
|
"github.com/status-im/simulation/propagation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO(divan): move this as variables to the frontend
|
||||||
const (
|
const (
|
||||||
// TODO(divan): move this as variables to the frontend
|
|
||||||
BlinkDecay = 100 * time.Millisecond // time for highlighted node/link to be active
|
BlinkDecay = 100 * time.Millisecond // time for highlighted node/link to be active
|
||||||
AnimationSlowdown = 1 // slowdown factor for propagation animation
|
AnimationSlowdown = 1 // slowdown factor for propagation animation
|
||||||
FPS = 30 // default FPS
|
FPS = 30 // default FPS
|
||||||
@ -42,7 +43,18 @@ func (w *WebGLScene) animate() {
|
|||||||
w.updatePositions()
|
w.updatePositions()
|
||||||
}
|
}
|
||||||
|
|
||||||
w.renderer.Render(w.scene, w.camera)
|
// some render throttling magic to prevent wasting CPU/GPU while idle
|
||||||
|
// if auto rotation or other effects are active, render always
|
||||||
|
var needRendering bool = w.wobble || w.autoRotate
|
||||||
|
if !needRendering {
|
||||||
|
// else, consult render throttler
|
||||||
|
needRendering = w.rt.NeedRendering()
|
||||||
|
}
|
||||||
|
|
||||||
|
if needRendering {
|
||||||
|
w.renderer.Render(w.scene, w.camera)
|
||||||
|
w.rt.ReenableIfNeeded()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToggleAutoRotation switches auto rotation option.
|
// ToggleAutoRotation switches auto rotation option.
|
||||||
@ -75,6 +87,7 @@ func (w *WebGLScene) BlinkEdge(id int) {
|
|||||||
// AnimatePropagation visualizes propagation of message based on plog.
|
// AnimatePropagation visualizes propagation of message based on plog.
|
||||||
func (w *WebGLScene) AnimatePropagation(plog *propagation.Log) {
|
func (w *WebGLScene) AnimatePropagation(plog *propagation.Log) {
|
||||||
fmt.Println("Animating plog")
|
fmt.Println("Animating plog")
|
||||||
|
w.rt.Disable()
|
||||||
for i, ts := range plog.Timestamps {
|
for i, ts := range plog.Timestamps {
|
||||||
duration := time.Duration(time.Duration(ts) * time.Millisecond)
|
duration := time.Duration(time.Duration(ts) * time.Millisecond)
|
||||||
duration = duration * AnimationSlowdown
|
duration = duration * AnimationSlowdown
|
||||||
@ -94,3 +107,17 @@ func (w *WebGLScene) AnimatePropagation(plog *propagation.Log) {
|
|||||||
time.AfterFunc(duration, fn)
|
time.AfterFunc(duration, fn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MouseMoveListener implements listener for mousemove events.
|
||||||
|
// We use it for disabling render throttling, as mousemove events
|
||||||
|
// correlates with user moving inside of the WebGL canvas. We
|
||||||
|
// may switch to use mousedown or drag events, but let's see how
|
||||||
|
// mousemove works.
|
||||||
|
// This is sort of a hack, as the proper approach would be to get
|
||||||
|
// data from controls code (w.controls.Update), but it's currently
|
||||||
|
// a JS code, so it's easier use this hack.
|
||||||
|
func (p *Page) MouseMoveListener(e *vecty.Event) {
|
||||||
|
if !p.webgl.rt.NeedRendering() {
|
||||||
|
p.webgl.rt.Disable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1
page.go
1
page.go
@ -105,6 +105,7 @@ func (p *Page) Render() vecty.ComponentOrHTML {
|
|||||||
),
|
),
|
||||||
vecty.Markup(
|
vecty.Markup(
|
||||||
event.KeyDown(p.KeyListener),
|
event.KeyDown(p.KeyListener),
|
||||||
|
event.MouseMove(p.MouseMoveListener),
|
||||||
event.VisibilityChange(p.VisibilityListener),
|
event.VisibilityChange(p.VisibilityListener),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
58
render_throttler.go
Normal file
58
render_throttler.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultRenderThrottleDecay defines a decay period after which throttling should reenabled.
|
||||||
|
const DefaultRenderThrottleDecay = 5 // sec
|
||||||
|
|
||||||
|
// RenderThrottler is a helper to enable rendering only when it's really need to.
|
||||||
|
// Rendering can be enabled/requested externally, but will be turned off/slowed down
|
||||||
|
// after some period of time.
|
||||||
|
// TODO(divan): disable/enable throttling is inverse of enable/disable rendering, and may be confusing. Rename maybe?
|
||||||
|
// TODO(divan): also, instead of disabling, might be a good idea to just decrease FPS. Explore this.
|
||||||
|
type RenderThrottler struct {
|
||||||
|
needRendering bool
|
||||||
|
lastUpdate int64
|
||||||
|
decay int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRenderThrottler returns a new render throttler.
|
||||||
|
func NewRenderThrottler() *RenderThrottler {
|
||||||
|
r := &RenderThrottler{
|
||||||
|
needRendering: true,
|
||||||
|
lastUpdate: time.Now().Unix(),
|
||||||
|
decay: DefaultRenderThrottleDecay,
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable disables render throttling.
|
||||||
|
func (r *RenderThrottler) Disable() {
|
||||||
|
fmt.Printf("Switching ON rendering")
|
||||||
|
r.needRendering = true
|
||||||
|
r.lastUpdate = time.Now().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable enables render throttling.
|
||||||
|
func (r *RenderThrottler) Enable() {
|
||||||
|
fmt.Printf("Switching off rendering")
|
||||||
|
r.needRendering = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReenableIfNeeded checks if sufficient time has passed since throttling has been
|
||||||
|
// disabled, and enables throttling back if so.
|
||||||
|
func (r *RenderThrottler) ReenableIfNeeded() {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
if r.lastUpdate+r.decay < now {
|
||||||
|
r.Enable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NeedRendering returns true if next frame should be rendered.
|
||||||
|
func (r *RenderThrottler) NeedRendering() bool {
|
||||||
|
return r.needRendering
|
||||||
|
}
|
6
scene.go
6
scene.go
@ -31,11 +31,15 @@ type WebGLScene struct {
|
|||||||
// TODO(divan): as soon as three.js wrappers allow us to access children, get rid of it here
|
// TODO(divan): as soon as three.js wrappers allow us to access children, get rid of it here
|
||||||
nodes []*Mesh
|
nodes []*Mesh
|
||||||
lines []*Line
|
lines []*Line
|
||||||
|
|
||||||
|
rt *RenderThrottler // used as a helper to reduce rendering calls when animation is not needed (experimental)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWebGLScene inits and returns new WebGL scene and canvas.
|
// NewWebGLScene inits and returns new WebGL scene and canvas.
|
||||||
func NewWebGLScene() *WebGLScene {
|
func NewWebGLScene() *WebGLScene {
|
||||||
w := &WebGLScene{}
|
w := &WebGLScene{
|
||||||
|
rt: NewRenderThrottler(),
|
||||||
|
}
|
||||||
w.WebGLRenderer = vthree.NewWebGLRenderer(vthree.WebGLOptions{
|
w.WebGLRenderer = vthree.NewWebGLRenderer(vthree.WebGLOptions{
|
||||||
Init: w.init,
|
Init: w.init,
|
||||||
Shutdown: w.shutdown,
|
Shutdown: w.shutdown,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user