forked from joejulian/gio
gpu: cache quad splitting and transform
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>
This commit is contained in:
+15
-10
@@ -5,6 +5,7 @@ package gpu
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"gioui.org/f32"
|
||||||
"gioui.org/internal/ops"
|
"gioui.org/internal/ops"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,11 +14,15 @@ type resourceCache struct {
|
|||||||
newRes map[interface{}]resource
|
newRes map[interface{}]resource
|
||||||
}
|
}
|
||||||
|
|
||||||
// opCache is like a resourceCache using the concrete Key
|
// opCache is like a resourceCache but using concrete types.
|
||||||
// key type to avoid allocations.
|
|
||||||
type opCache struct {
|
type opCache struct {
|
||||||
res map[ops.Key]resource
|
res map[ops.Key]opCacheValue
|
||||||
newRes map[ops.Key]resource
|
newRes map[ops.Key]opCacheValue
|
||||||
|
}
|
||||||
|
|
||||||
|
type opCacheValue struct {
|
||||||
|
data *pathData
|
||||||
|
bounds f32.Rectangle
|
||||||
}
|
}
|
||||||
|
|
||||||
func newResourceCache() *resourceCache {
|
func newResourceCache() *resourceCache {
|
||||||
@@ -66,12 +71,12 @@ func (r *resourceCache) release() {
|
|||||||
|
|
||||||
func newOpCache() *opCache {
|
func newOpCache() *opCache {
|
||||||
return &opCache{
|
return &opCache{
|
||||||
res: make(map[ops.Key]resource),
|
res: make(map[ops.Key]opCacheValue),
|
||||||
newRes: make(map[ops.Key]resource),
|
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]
|
v, exists := r.res[key]
|
||||||
if exists {
|
if exists {
|
||||||
r.newRes[key] = v
|
r.newRes[key] = v
|
||||||
@@ -79,7 +84,7 @@ func (r *opCache) get(key ops.Key) (resource, bool) {
|
|||||||
return v, exists
|
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 {
|
if _, exists := r.newRes[key]; exists {
|
||||||
panic(fmt.Errorf("key exists, %#v", key))
|
panic(fmt.Errorf("key exists, %#v", key))
|
||||||
}
|
}
|
||||||
@@ -91,7 +96,7 @@ func (r *opCache) frame() {
|
|||||||
for k, v := range r.res {
|
for k, v := range r.res {
|
||||||
if _, exists := r.newRes[k]; !exists {
|
if _, exists := r.newRes[k]; !exists {
|
||||||
delete(r.res, k)
|
delete(r.res, k)
|
||||||
v.release()
|
v.data.release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for k, v := range r.newRes {
|
for k, v := range r.newRes {
|
||||||
@@ -102,7 +107,7 @@ func (r *opCache) frame() {
|
|||||||
|
|
||||||
func (r *opCache) release() {
|
func (r *opCache) release() {
|
||||||
for _, v := range r.newRes {
|
for _, v := range r.newRes {
|
||||||
v.release()
|
v.data.release()
|
||||||
}
|
}
|
||||||
r.newRes = nil
|
r.newRes = nil
|
||||||
r.res = nil
|
r.res = nil
|
||||||
|
|||||||
+27
-17
@@ -29,8 +29,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type GPU struct {
|
type GPU struct {
|
||||||
pathCache *opCache
|
cache *resourceCache
|
||||||
cache *resourceCache
|
|
||||||
|
|
||||||
defFBO backend.Framebuffer
|
defFBO backend.Framebuffer
|
||||||
profile string
|
profile string
|
||||||
@@ -65,6 +64,7 @@ type drawOps struct {
|
|||||||
pathOps []*pathOp
|
pathOps []*pathOp
|
||||||
pathOpCache []pathOp
|
pathOpCache []pathOp
|
||||||
qs quadSplitter
|
qs quadSplitter
|
||||||
|
pathCache *opCache
|
||||||
uniqueKeyCounter int
|
uniqueKeyCounter int
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +86,7 @@ type pathOp struct {
|
|||||||
// clip is the union of all
|
// clip is the union of all
|
||||||
// later clip rectangles.
|
// later clip rectangles.
|
||||||
clip image.Rectangle
|
clip image.Rectangle
|
||||||
|
bounds f32.Rectangle
|
||||||
pathKey ops.Key
|
pathKey ops.Key
|
||||||
path bool
|
path bool
|
||||||
pathVerts []byte
|
pathVerts []byte
|
||||||
@@ -277,10 +278,10 @@ const (
|
|||||||
func New(ctx backend.Device) (*GPU, error) {
|
func New(ctx backend.Device) (*GPU, error) {
|
||||||
defFBO := ctx.CurrentFramebuffer()
|
defFBO := ctx.CurrentFramebuffer()
|
||||||
g := &GPU{
|
g := &GPU{
|
||||||
defFBO: defFBO,
|
defFBO: defFBO,
|
||||||
pathCache: newOpCache(),
|
cache: newResourceCache(),
|
||||||
cache: newResourceCache(),
|
|
||||||
}
|
}
|
||||||
|
g.drawOps.pathCache = newOpCache()
|
||||||
if err := g.init(ctx); err != nil {
|
if err := g.init(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -295,7 +296,7 @@ func (g *GPU) init(ctx backend.Device) error {
|
|||||||
|
|
||||||
func (g *GPU) Release() {
|
func (g *GPU) Release() {
|
||||||
g.renderer.release()
|
g.renderer.release()
|
||||||
g.pathCache.release()
|
g.drawOps.pathCache.release()
|
||||||
g.cache.release()
|
g.cache.release()
|
||||||
if g.timers != nil {
|
if g.timers != nil {
|
||||||
g.timers.release()
|
g.timers.release()
|
||||||
@@ -316,9 +317,12 @@ func (g *GPU) Collect(viewport image.Point, frameOps *op.Ops) {
|
|||||||
g.cleanupTimer = g.timers.newTimer()
|
g.cleanupTimer = g.timers.newTimer()
|
||||||
}
|
}
|
||||||
for _, p := range g.drawOps.pathOps {
|
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)
|
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
|
p.pathVerts = nil
|
||||||
}
|
}
|
||||||
@@ -344,7 +348,7 @@ func (g *GPU) BeginFrame() {
|
|||||||
g.stencilTimer.begin()
|
g.stencilTimer.begin()
|
||||||
g.ctx.SetBlend(true)
|
g.ctx.SetBlend(true)
|
||||||
g.renderer.packStencils(&g.drawOps.pathOps)
|
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.packIntersections(g.drawOps.imageOps)
|
||||||
g.renderer.intersect(g.drawOps.imageOps)
|
g.renderer.intersect(g.drawOps.imageOps)
|
||||||
g.stencilTimer.end()
|
g.stencilTimer.end()
|
||||||
@@ -361,7 +365,7 @@ func (g *GPU) BeginFrame() {
|
|||||||
func (g *GPU) EndFrame() {
|
func (g *GPU) EndFrame() {
|
||||||
g.cleanupTimer.begin()
|
g.cleanupTimer.begin()
|
||||||
g.cache.frame()
|
g.cache.frame()
|
||||||
g.pathCache.frame()
|
g.drawOps.pathCache.frame()
|
||||||
g.cleanupTimer.end()
|
g.cleanupTimer.end()
|
||||||
if g.drawOps.profile && g.timers.ready() {
|
if g.drawOps.profile && g.timers.ready() {
|
||||||
zt, st, covt, cleant := g.zopsTimer.Elapsed, g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed
|
zt, st, covt, cleant := g.zopsTimer.Elapsed, g.stencilTimer.Elapsed, g.coverTimer.Elapsed, g.cleanupTimer.Elapsed
|
||||||
@@ -505,8 +509,8 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
|
|||||||
r.ctx.BindFramebuffer(f.fbo)
|
r.ctx.BindFramebuffer(f.fbo)
|
||||||
r.ctx.Clear(0.0, 0.0, 0.0, 0.0)
|
r.ctx.Clear(0.0, 0.0, 0.0, 0.0)
|
||||||
}
|
}
|
||||||
data, _ := pathCache.get(p.pathKey)
|
v, _ := pathCache.get(p.pathKey)
|
||||||
r.pather.stencilPath(p.clip, p.place.Pos, data.(*pathData))
|
r.pather.stencilPath(p.clip, p.place.Pos, v.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,10 +685,11 @@ func (d *drawOps) newPathOp() *pathOp {
|
|||||||
return &d.pathOpCache[len(d.pathOpCache)-1]
|
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 := d.newPathOp()
|
||||||
*npath = pathOp{
|
*npath = pathOp{
|
||||||
parent: state.cpath,
|
parent: state.cpath,
|
||||||
|
bounds: bounds,
|
||||||
}
|
}
|
||||||
state.cpath = npath
|
state.cpath = npath
|
||||||
if len(aux) > 0 {
|
if len(aux) > 0 {
|
||||||
@@ -724,9 +729,14 @@ loop:
|
|||||||
if len(aux) > 0 {
|
if len(aux) > 0 {
|
||||||
// There is a clipping path, build the gpu data and update the
|
// 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
|
// cache key such that it will be equal only if the transform is the
|
||||||
// same also.
|
// same also. Use cached data if we have it.
|
||||||
aux, op.bounds = d.buildVerts(aux, state.t)
|
|
||||||
auxKey = auxKey.SetTransform(state.t)
|
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 {
|
} else {
|
||||||
aux, op.bounds, _ = d.boundsForTransformedRect(bounds, state.t)
|
aux, op.bounds, _ = d.boundsForTransformedRect(bounds, state.t)
|
||||||
auxKey = d.noCacheKey()
|
auxKey = d.noCacheKey()
|
||||||
@@ -736,7 +746,7 @@ loop:
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
d.addClipPath(&state, aux, auxKey)
|
d.addClipPath(&state, aux, auxKey, op.bounds)
|
||||||
aux = nil
|
aux = nil
|
||||||
auxKey = ops.Key{}
|
auxKey = ops.Key{}
|
||||||
case opconst.TypeColor:
|
case opconst.TypeColor:
|
||||||
@@ -760,7 +770,7 @@ loop:
|
|||||||
if clipData != nil {
|
if clipData != nil {
|
||||||
// The paint operation is sheared or rotated, add a clip path representing
|
// The paint operation is sheared or rotated, add a clip path representing
|
||||||
// this transformed rectangle.
|
// this transformed rectangle.
|
||||||
d.addClipPath(&state, clipData, d.noCacheKey())
|
d.addClipPath(&state, clipData, d.noCacheKey(), bnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
bounds := boundRectF(clip)
|
bounds := boundRectF(clip)
|
||||||
|
|||||||
Reference in New Issue
Block a user