Skip to content
This repository has been archived by the owner on Jan 16, 2024. It is now read-only.

Commit

Permalink
gpu: cache quad splitting and transform
Browse files Browse the repository at this point in the history
Cache also CPU operations by moving pathCache into
drawOps and use it in collectOps to avoid splitting and
transformation of quads if in cache. In order to support
this use a concrete type in opCache instead of interface.

Signed-off-by: Viktor <viktor.ogeman@gmail.com>
  • Loading branch information
vron authored and eliasnaur committed Jun 21, 2020
1 parent e3bb94e commit 380938c
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 27 deletions.
25 changes: 15 additions & 10 deletions gpu/caches.go
Expand Up @@ -5,6 +5,7 @@ package gpu
import (
"fmt"

"gioui.org/f32"
"gioui.org/internal/ops"
)

Expand All @@ -13,11 +14,15 @@ type resourceCache struct {
newRes map[interface{}]resource
}

// opCache is like a resourceCache using the concrete Key
// key type to avoid allocations.
// opCache is like a resourceCache but using concrete types.
type opCache struct {
res map[ops.Key]resource
newRes map[ops.Key]resource
res map[ops.Key]opCacheValue
newRes map[ops.Key]opCacheValue
}

type opCacheValue struct {
data *pathData
bounds f32.Rectangle
}

func newResourceCache() *resourceCache {
Expand Down Expand Up @@ -66,20 +71,20 @@ func (r *resourceCache) release() {

func newOpCache() *opCache {
return &opCache{
res: make(map[ops.Key]resource),
newRes: make(map[ops.Key]resource),
res: make(map[ops.Key]opCacheValue),
newRes: make(map[ops.Key]opCacheValue),
}
}

func (r *opCache) get(key ops.Key) (resource, bool) {
func (r *opCache) get(key ops.Key) (opCacheValue, bool) {
v, exists := r.res[key]
if exists {
r.newRes[key] = v
}
return v, exists
}

func (r *opCache) put(key ops.Key, val resource) {
func (r *opCache) put(key ops.Key, val opCacheValue) {
if _, exists := r.newRes[key]; exists {
panic(fmt.Errorf("key exists, %#v", key))
}
Expand All @@ -91,7 +96,7 @@ func (r *opCache) frame() {
for k, v := range r.res {
if _, exists := r.newRes[k]; !exists {
delete(r.res, k)
v.release()
v.data.release()
}
}
for k, v := range r.newRes {
Expand All @@ -102,7 +107,7 @@ func (r *opCache) frame() {

func (r *opCache) release() {
for _, v := range r.newRes {
v.release()
v.data.release()
}
r.newRes = nil
r.res = nil
Expand Down
44 changes: 27 additions & 17 deletions gpu/gpu.go
Expand Up @@ -29,8 +29,7 @@ import (
)

type GPU struct {
pathCache *opCache
cache *resourceCache
cache *resourceCache

defFBO backend.Framebuffer
profile string
Expand Down Expand Up @@ -65,6 +64,7 @@ type drawOps struct {
pathOps []*pathOp
pathOpCache []pathOp
qs quadSplitter
pathCache *opCache
uniqueKeyCounter int
}

Expand All @@ -86,6 +86,7 @@ type pathOp struct {
// clip is the union of all
// later clip rectangles.
clip image.Rectangle
bounds f32.Rectangle
pathKey ops.Key
path bool
pathVerts []byte
Expand Down Expand Up @@ -277,10 +278,10 @@ const (
func New(ctx backend.Device) (*GPU, error) {
defFBO := ctx.CurrentFramebuffer()
g := &GPU{
defFBO: defFBO,
pathCache: newOpCache(),
cache: newResourceCache(),
defFBO: defFBO,
cache: newResourceCache(),
}
g.drawOps.pathCache = newOpCache()
if err := g.init(ctx); err != nil {
return nil, err
}
Expand All @@ -295,7 +296,7 @@ func (g *GPU) init(ctx backend.Device) error {

func (g *GPU) Release() {
g.renderer.release()
g.pathCache.release()
g.drawOps.pathCache.release()
g.cache.release()
if g.timers != nil {
g.timers.release()
Expand All @@ -316,9 +317,12 @@ func (g *GPU) Collect(viewport image.Point, frameOps *op.Ops) {
g.cleanupTimer = g.timers.newTimer()
}
for _, p := range g.drawOps.pathOps {
if _, exists := g.pathCache.get(p.pathKey); !exists {
if _, exists := g.drawOps.pathCache.get(p.pathKey); !exists {
data := buildPath(g.ctx, p.pathVerts)
g.pathCache.put(p.pathKey, data)
g.drawOps.pathCache.put(p.pathKey, opCacheValue{
data: data,
bounds: p.bounds,
})
}
p.pathVerts = nil
}
Expand All @@ -344,7 +348,7 @@ func (g *GPU) BeginFrame() {
g.stencilTimer.begin()
g.ctx.SetBlend(true)
g.renderer.packStencils(&g.drawOps.pathOps)
g.renderer.stencilClips(g.pathCache, g.drawOps.pathOps)
g.renderer.stencilClips(g.drawOps.pathCache, g.drawOps.pathOps)
g.renderer.packIntersections(g.drawOps.imageOps)
g.renderer.intersect(g.drawOps.imageOps)
g.stencilTimer.end()
Expand All @@ -361,7 +365,7 @@ func (g *GPU) BeginFrame() {
func (g *GPU) EndFrame() {
g.cleanupTimer.begin()
g.cache.frame()
g.pathCache.frame()
g.drawOps.pathCache.frame()
g.cleanupTimer.end()
if g.drawOps.profile && g.timers.ready() {
zt, st, covt, cleant := g.zopsTimer.Elapsed, g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed
Expand Down Expand Up @@ -505,8 +509,8 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
r.ctx.BindFramebuffer(f.fbo)
r.ctx.Clear(0.0, 0.0, 0.0, 0.0)
}
data, _ := pathCache.get(p.pathKey)
r.pather.stencilPath(p.clip, p.place.Pos, data.(*pathData))
v, _ := pathCache.get(p.pathKey)
r.pather.stencilPath(p.clip, p.place.Pos, v.data)
}
}

Expand Down Expand Up @@ -681,10 +685,11 @@ func (d *drawOps) newPathOp() *pathOp {
return &d.pathOpCache[len(d.pathOpCache)-1]
}

func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey ops.Key) {
func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey ops.Key, bounds f32.Rectangle) {
npath := d.newPathOp()
*npath = pathOp{
parent: state.cpath,
bounds: bounds,
}
state.cpath = npath
if len(aux) > 0 {
Expand Down Expand Up @@ -724,9 +729,14 @@ loop:
if len(aux) > 0 {
// There is a clipping path, build the gpu data and update the
// cache key such that it will be equal only if the transform is the
// same also.
aux, op.bounds = d.buildVerts(aux, state.t)
// same also. Use cached data if we have it.
auxKey = auxKey.SetTransform(state.t)
if v, ok := d.pathCache.get(auxKey); ok {
// Since the GPU data exists in the cache aux will not be used.
op.bounds = v.bounds
} else {
aux, op.bounds = d.buildVerts(aux, state.t)
}
} else {
aux, op.bounds, _ = d.boundsForTransformedRect(bounds, state.t)
auxKey = d.noCacheKey()
Expand All @@ -736,7 +746,7 @@ loop:
continue
}

d.addClipPath(&state, aux, auxKey)
d.addClipPath(&state, aux, auxKey, op.bounds)
aux = nil
auxKey = ops.Key{}
case opconst.TypeColor:
Expand All @@ -760,7 +770,7 @@ loop:
if clipData != nil {
// The paint operation is sheared or rotated, add a clip path representing
// this transformed rectangle.
d.addClipPath(&state, clipData, d.noCacheKey())
d.addClipPath(&state, clipData, d.noCacheKey(), bnd)
}

bounds := boundRectF(clip)
Expand Down

0 comments on commit 380938c

Please sign in to comment.