From 6069b279ad99db62ceb090004fd3109602e93317 Mon Sep 17 00:00:00 2001 From: Ben Clayton Date: Tue, 4 Aug 2015 17:53:00 +0100 Subject: [PATCH] Rip out refCounted from the driver. Instead, use a mark-and-sweep per context. Removes the horrible Release() interface method from Texture and Canvas. GXUI seems a lot snappier, probably because we're not ref-twiddling everything any more. Fixes #111 --- canvas.go | 1 - drivers/gl/blitter.go | 2 - drivers/gl/canvas.go | 47 ---------------------- drivers/gl/context.go | 41 ++++++++++++------- drivers/gl/glyph_page.go | 9 ++--- drivers/gl/index_buffer.go | 26 +++++------- drivers/gl/refcounted.go | 2 + drivers/gl/shape.go | 21 ---------- drivers/gl/stats.go | 78 +++++++++++++++++++++++------------- drivers/gl/texture.go | 9 ++--- drivers/gl/vertex_buffer.go | 14 ------- drivers/gl/vertex_stream.go | 26 +++++------- drivers/gl/viewport.go | 13 +----- mixins/parts/draw_paint.go | 9 ----- mixins/window.go | 1 - texture.go | 5 +-- themes/basic/progress_bar.go | 6 +-- 17 files changed, 106 insertions(+), 204 deletions(-) diff --git a/canvas.go b/canvas.go index aa95d92..48d57f0 100644 --- a/canvas.go +++ b/canvas.go @@ -23,5 +23,4 @@ type Canvas interface { DrawPolygon(Polygon, Pen, Brush) DrawRect(math.Rect, Brush) DrawRoundedRect(rect math.Rect, tl, tr, bl, br float32, p Pen, b Brush) - Release() } diff --git a/drivers/gl/blitter.go b/drivers/gl/blitter.go index 07fbac4..75e5c06 100644 --- a/drivers/gl/blitter.go +++ b/drivers/gl/blitter.go @@ -119,7 +119,6 @@ func newBlitter(ctx *context, stats *contextStats) *blitter { } func (b *blitter) destroy(ctx *context) { - b.quad.release() b.copyShader.destroy(ctx) b.colorShader.destroy(ctx) b.fontShader.destroy(ctx) @@ -297,7 +296,6 @@ func (b *blitter) commitGlyphs(ctx *context) { "mSrc": mSrc, }) gl.Enable(gl.SCISSOR_TEST) - s.release() b.glyphBatch.GlyphPage = nil b.glyphBatch.DstRects = b.glyphBatch.DstRects[:0] b.glyphBatch.SrcRects = b.glyphBatch.SrcRects[:0] diff --git a/drivers/gl/canvas.go b/drivers/gl/canvas.go index 7aeae71..ba25737 100644 --- a/drivers/gl/canvas.go +++ b/drivers/gl/canvas.go @@ -32,15 +32,8 @@ type drawState struct { OriginPixels math.Point } -type resource interface { - addRef() - release() bool -} - type canvas struct { - refCounted sizeDips math.Size - resources []resource ops []canvasOp built bool buildingPushCount int @@ -53,13 +46,10 @@ func newCanvas(sizeDips math.Size) *canvas { c := &canvas{ sizeDips: sizeDips, } - c.init() - globalStats.canvasCount.inc() return c } func (c *canvas) draw(ctx *context, dss *drawStateStack) { - c.assertAlive("draw") ds := dss.head() ctx.apply(ds) @@ -69,31 +59,12 @@ func (c *canvas) draw(ctx *context, dss *drawStateStack) { } func (c *canvas) appendOp(name string, op canvasOp) { - c.assertAlive(name) if c.built { panic(fmt.Errorf("%s() called after Complete()", name)) } c.ops = append(c.ops, op) } -func (c *canvas) appendResource(r resource) { - r.addRef() - c.resources = append(c.resources, r) -} - -func (c *canvas) release() bool { - if !c.refCounted.release() { - return false - } - for _, r := range c.resources { - r.release() - } - c.ops = nil - c.resources = nil - globalStats.canvasCount.dec() - return true -} - // gxui.Canvas compliance func (c *canvas) Size() math.Size { return c.sizeDips @@ -164,7 +135,6 @@ func (c *canvas) DrawCanvas(cc gxui.Canvas, offsetDips math.Point) { dss.pop() ctx.apply(dss.head()) }) - c.appendResource(childCanvas) } func (c *canvas) DrawRunes(f gxui.Font, r []rune, p []math.Point, col gxui.Color) { @@ -186,10 +156,6 @@ func (c *canvas) DrawLines(lines gxui.Polygon, pen gxui.Pen) { ctx.blitter.blitShape(ctx, *edge, pen.Color, ds) } }) - if edge != nil { - c.appendResource(edge) - edge.release() - } } func (c *canvas) DrawPolygon(poly gxui.Polygon, pen gxui.Pen, brush gxui.Brush) { @@ -203,14 +169,6 @@ func (c *canvas) DrawPolygon(poly gxui.Polygon, pen gxui.Pen, brush gxui.Brush) ctx.blitter.blitShape(ctx, *edge, pen.Color, ds) } }) - if fill != nil { - c.appendResource(fill) - fill.release() - } - if edge != nil { - c.appendResource(edge) - edge.release() - } } func (c *canvas) DrawRect(r math.Rect, brush gxui.Brush) { @@ -242,9 +200,4 @@ func (c *canvas) DrawTexture(t gxui.Texture, r math.Rect) { tc := ctx.getOrCreateTextureContext(t.(*texture)) ctx.blitter.blit(ctx, tc, tc.sizePixels.Rect(), ctx.resolution.rectDipsToPixels(r), dss.head()) }) - c.appendResource(t.(*texture)) -} - -func (c *canvas) Release() { - c.release() } diff --git a/drivers/gl/context.go b/drivers/gl/context.go index 449c784..2b7a26f 100644 --- a/drivers/gl/context.go +++ b/drivers/gl/context.go @@ -10,6 +10,12 @@ import ( "github.com/goxjs/gl" ) +// contextResource is used as an anonymous field by types that are constructed +// per context. +type contextResource struct { + lastContextUse int // used for mark-and-sweeping the resource. +} + type context struct { blitter *blitter resolution resolution @@ -19,6 +25,7 @@ type context struct { indexBufferContexts map[*indexBuffer]*indexBufferContext sizeDips, sizePixels math.Size clip math.Rect + frame int } func newContext() *context { @@ -52,42 +59,43 @@ func (c *context) destroy() { } func (c *context) beginDraw(sizeDips, sizePixels math.Size) { - // Reap any dead textures + dipsToPixels := float32(sizePixels.W) / float32(sizeDips.W) + + c.sizeDips = sizeDips + c.sizePixels = sizePixels + c.resolution = resolution(dipsToPixels*65536 + 0.5) + + c.stats.drawCallCount = 0 + c.stats.timer("Frame").start() +} + +func (c *context) endDraw() { + // Reap any unused resources for texture, tc := range c.textureContexts { - if !texture.alive() { + if tc.lastContextUse != c.frame { delete(c.textureContexts, texture) tc.destroy() c.stats.textureCount-- } } for stream, sc := range c.vertexStreamContexts { - if !stream.alive() { + if sc.lastContextUse != c.frame { delete(c.vertexStreamContexts, stream) sc.destroy() c.stats.vertexStreamCount-- } } for buffer, ic := range c.indexBufferContexts { - if !buffer.alive() { + if ic.lastContextUse != c.frame { delete(c.indexBufferContexts, buffer) ic.destroy() c.stats.indexBufferCount-- } } - dipsToPixels := float32(sizePixels.W) / float32(sizeDips.W) - - c.sizeDips = sizeDips - c.sizePixels = sizePixels - c.resolution = resolution(dipsToPixels*65536 + 0.5) - - c.stats.drawCallCount = 0 - c.stats.timer("Frame").start() -} - -func (c *context) endDraw() { c.stats.timer("Frame").stop() c.stats.frameCount++ + c.frame++ } func (c *context) getOrCreateTextureContext(t *texture) *textureContext { @@ -97,6 +105,7 @@ func (c *context) getOrCreateTextureContext(t *texture) *textureContext { c.textureContexts[t] = tc c.stats.textureCount++ } + tc.lastContextUse = c.frame return tc } @@ -107,6 +116,7 @@ func (c *context) getOrCreateVertexStreamContext(vs *vertexStream) *vertexStream c.vertexStreamContexts[vs] = vc c.stats.vertexStreamCount++ } + vc.lastContextUse = c.frame return vc } @@ -117,6 +127,7 @@ func (c *context) getOrCreateIndexBufferContext(ib *indexBuffer) *indexBufferCon c.indexBufferContexts[ib] = ic c.stats.indexBufferCount++ } + ic.lastContextUse = c.frame return ic } diff --git a/drivers/gl/glyph_page.go b/drivers/gl/glyph_page.go index 0e21f76..905a0a1 100644 --- a/drivers/gl/glyph_page.go +++ b/drivers/gl/glyph_page.go @@ -5,11 +5,12 @@ package gl import ( - "github.com/google/gxui/math" "image" "image/png" "os" + "github.com/google/gxui/math" + "code.google.com/p/freetype-go/freetype/raster" "code.google.com/p/freetype-go/freetype/truetype" ) @@ -158,11 +159,7 @@ func (p *glyphPage) add(rune rune, g *glyph) bool { if h > p.rowHeight { p.rowHeight = h } - - if p.tex != nil { - p.tex.Release() - p.tex = nil - } + p.tex = nil return true } diff --git a/drivers/gl/index_buffer.go b/drivers/gl/index_buffer.go index dffb4e3..172c225 100644 --- a/drivers/gl/index_buffer.go +++ b/drivers/gl/index_buffer.go @@ -12,17 +12,10 @@ import ( ) type indexBuffer struct { - refCounted data []byte ty primitiveType } -type indexBufferContext struct { - buffer gl.Buffer - ty primitiveType - length int -} - func newIndexBuffer(ty primitiveType, data16 []uint16) *indexBuffer { switch ty { case ptUbyte, ptUshort, ptUint: @@ -44,19 +37,9 @@ func newIndexBuffer(ty primitiveType, data16 []uint16) *indexBuffer { data: data, ty: ty, } - ib.init() - globalStats.indexBufferCount.inc() return ib } -func (b *indexBuffer) release() bool { - if !b.refCounted.release() { - return false - } - globalStats.indexBufferCount.dec() - return true -} - func (b *indexBuffer) newContext() *indexBufferContext { dataVal := reflect.ValueOf(b.data) length := dataVal.Len() / 2 // HACK: Hardcode support for only ptUshort. @@ -67,6 +50,7 @@ func (b *indexBuffer) newContext() *indexBufferContext { gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Buffer{}) checkError() + globalStats.indexBufferContextCount.inc() return &indexBufferContext{ buffer: buffer, ty: b.ty, @@ -74,7 +58,15 @@ func (b *indexBuffer) newContext() *indexBufferContext { } } +type indexBufferContext struct { + contextResource + buffer gl.Buffer + ty primitiveType + length int +} + func (c *indexBufferContext) destroy() { + globalStats.indexBufferContextCount.dec() gl.DeleteBuffer(c.buffer) c.buffer = gl.Buffer{} } diff --git a/drivers/gl/refcounted.go b/drivers/gl/refcounted.go index 86a069f..4df427a 100644 --- a/drivers/gl/refcounted.go +++ b/drivers/gl/refcounted.go @@ -4,6 +4,7 @@ package gl +/* import ( "fmt" "runtime" @@ -72,3 +73,4 @@ func (r *refCounted) assertAlive(funcName string) { } } } +*/ diff --git a/drivers/gl/shape.go b/drivers/gl/shape.go index 7592a7a..ffc1d82 100644 --- a/drivers/gl/shape.go +++ b/drivers/gl/shape.go @@ -7,7 +7,6 @@ package gl import "github.com/goxjs/gl" type shape struct { - refCounted vb *vertexBuffer ib *indexBuffer drawMode drawMode @@ -23,27 +22,9 @@ func newShape(vb *vertexBuffer, ib *indexBuffer, drawMode drawMode) *shape { ib: ib, drawMode: drawMode, } - s.init() - globalStats.shapeCount.inc() return s } -func (s *shape) release() bool { - if !s.refCounted.release() { - return false - } - if s.vb != nil { - s.vb.release() - s.vb = nil - } - if s.ib != nil { - s.ib.release() - s.ib = nil - } - globalStats.shapeCount.dec() - return true -} - func newQuadShape() *shape { pos := newVertexStream("aPosition", stFloatVec2, []float32{ 0.0, 0.0, @@ -60,8 +41,6 @@ func newQuadShape() *shape { } func (s shape) draw(ctx *context, shader *shaderProgram, ub uniformBindings) { - s.assertAlive("draw") - shader.bind(ctx, s.vb, ub) if s.ib != nil { ctx.getOrCreateIndexBufferContext(s.ib).render(s.drawMode) diff --git a/drivers/gl/stats.go b/drivers/gl/stats.go index be6775e..8c9333b 100644 --- a/drivers/gl/stats.go +++ b/drivers/gl/stats.go @@ -11,35 +11,63 @@ import ( "time" ) -const historySize = 100 +const ( + printGlobalStats = false + historySize = 100 +) + +func init() { + if printGlobalStats { + go func() { + for { + time.Sleep(time.Second) + println(globalStats.get()) + } + }() + } +} -type count int32 +type count struct { + value int32 + incs int32 + decs int32 +} func (c *count) inc() { - atomic.AddInt32((*int32)(c), 1) + atomic.AddInt32(&c.value, 1) + atomic.AddInt32(&c.incs, 1) } func (c *count) dec() { - if atomic.AddInt32((*int32)(c), -1) < 0 { + atomic.AddInt32(&c.decs, 1) + if atomic.AddInt32(&c.value, -1) < 0 { panic("Count has gone negative") } } +func (c *count) resetDeltas() { + atomic.StoreInt32(&c.incs, 0) + atomic.StoreInt32(&c.decs, 0) +} + +func (c count) String() string { + return fmt.Sprintf("%d [+%d/-%d]", c.value, c.incs, c.decs) +} + type globalDriverStats struct { - canvasCount count - shapeCount count - vertexBufferCount count - vertexStreamCount count - indexBufferCount count + vertexStreamContextCount count + indexBufferContextCount count + textureContextCount count } -func (s globalDriverStats) String() string { +func (s *globalDriverStats) get() string { buffer := &bytes.Buffer{} - fmt.Fprintf(buffer, "Canvas count: %d\n", s.canvasCount) - fmt.Fprintf(buffer, "Shape count: %d\n", s.shapeCount) - fmt.Fprintf(buffer, "Vertex buffer count: %d\n", s.vertexBufferCount) - fmt.Fprintf(buffer, "Vertex stream count: %d\n", s.vertexStreamCount) - fmt.Fprintf(buffer, "Index buffer count: %d\n", s.indexBufferCount) + fmt.Fprintf(buffer, "Vertex stream context count: %v\n", s.vertexStreamContextCount) + fmt.Fprintf(buffer, "Index buffer context count: %v\n", s.indexBufferContextCount) + fmt.Fprintf(buffer, "Texture context count: %v\n", s.textureContextCount) + s.vertexStreamContextCount.resetDeltas() + s.indexBufferContextCount.resetDeltas() + s.textureContextCount.resetDeltas() return buffer.String() } @@ -86,16 +114,13 @@ func (t timer) Format(f fmt.State, c rune) { } type contextStats struct { - textureCount int - framebufferUsedCount int - framebufferFreeCount int - framebufferBytesAllocated int - vertexStreamCount int - indexBufferCount int - shaderProgramCount int - frameCount int - drawCallCount int - timers []timer + textureCount int + vertexStreamCount int + indexBufferCount int + shaderProgramCount int + frameCount int + drawCallCount int + timers []timer } func (s *contextStats) timer(name string) *timer { @@ -117,9 +142,6 @@ func (s contextStats) String() string { fmt.Fprintf(buffer, "Draw calls per frame: %d\n", s.drawCallCount) fmt.Fprintf(buffer, "Frame count: %d\n", s.frameCount) fmt.Fprintf(buffer, "Textures: %d\n", s.textureCount) - fmt.Fprintf(buffer, "Framebuffers Used: %d\n", s.framebufferUsedCount) - fmt.Fprintf(buffer, "Framebuffers Free: %d\n", s.framebufferFreeCount) - fmt.Fprintf(buffer, "Framebuffer bytes allocated: %d\n", s.framebufferBytesAllocated) fmt.Fprintf(buffer, "Vertex stream count: %d\n", s.vertexStreamCount) fmt.Fprintf(buffer, "Index buffer count: %d\n", s.indexBufferCount) fmt.Fprintf(buffer, "Shader program count: %d\n", s.shaderProgramCount) diff --git a/drivers/gl/texture.go b/drivers/gl/texture.go index 5d58267..16a0df8 100644 --- a/drivers/gl/texture.go +++ b/drivers/gl/texture.go @@ -12,7 +12,6 @@ import ( ) type texture struct { - refCounted image image.Image pixelsPerDip float32 flipY bool @@ -23,7 +22,6 @@ func newTexture(img image.Image, pixelsPerDip float32) *texture { image: img, pixelsPerDip: pixelsPerDip, } - t.init() return t } @@ -49,10 +47,6 @@ func (t *texture) SetFlipY(flipY bool) { t.flipY = flipY } -func (t *texture) Release() { - t.release() -} - func (t *texture) newContext() *textureContext { var fmt gl.Enum var data []byte @@ -82,6 +76,7 @@ func (t *texture) newContext() *textureContext { gl.BindTexture(gl.TEXTURE_2D, gl.Texture{}) checkError() + globalStats.textureContextCount.inc() return &textureContext{ texture: texture, sizePixels: t.Size(), @@ -91,6 +86,7 @@ func (t *texture) newContext() *textureContext { } type textureContext struct { + contextResource texture gl.Texture sizePixels math.Size flipY bool @@ -98,6 +94,7 @@ type textureContext struct { } func (c *textureContext) destroy() { + globalStats.textureContextCount.dec() gl.DeleteTexture(c.texture) c.texture = gl.Texture{} } diff --git a/drivers/gl/vertex_buffer.go b/drivers/gl/vertex_buffer.go index b4cc536..b1284a0 100644 --- a/drivers/gl/vertex_buffer.go +++ b/drivers/gl/vertex_buffer.go @@ -7,7 +7,6 @@ package gl import "fmt" type vertexBuffer struct { - refCounted streams map[string]*vertexStream count int } @@ -16,7 +15,6 @@ func newVertexBuffer(streams ...*vertexStream) *vertexBuffer { vb := &vertexBuffer{ streams: map[string]*vertexStream{}, } - vb.init() for i, s := range streams { if i == 0 { vb.count = s.count @@ -28,17 +26,5 @@ func newVertexBuffer(streams ...*vertexStream) *vertexBuffer { } vb.streams[s.name] = s } - globalStats.vertexBufferCount.inc() return vb } - -func (vb *vertexBuffer) release() bool { - if !vb.refCounted.release() { - return false - } - for _, s := range vb.streams { - s.release() - } - globalStats.vertexBufferCount.dec() - return true -} diff --git a/drivers/gl/vertex_stream.go b/drivers/gl/vertex_stream.go index 84e842c..343190e 100644 --- a/drivers/gl/vertex_stream.go +++ b/drivers/gl/vertex_stream.go @@ -13,17 +13,12 @@ import ( ) type vertexStream struct { - refCounted name string data []byte ty shaderDataType count int } -type vertexStreamContext struct { - buffer gl.Buffer -} - func newVertexStream(name string, ty shaderDataType, data32 []float32) *vertexStream { dataVal := reflect.ValueOf(data32) dataLen := dataVal.Len() @@ -45,19 +40,9 @@ func newVertexStream(name string, ty shaderDataType, data32 []float32) *vertexSt ty: ty, count: dataLen / ty.vectorElementCount(), } - vs.init() - globalStats.vertexStreamCount.inc() return vs } -func (s *vertexStream) release() bool { - if !s.refCounted.release() { - return false - } - globalStats.vertexStreamCount.dec() - return true -} - func (s *vertexStream) newContext() *vertexStreamContext { buffer := gl.CreateBuffer() gl.BindBuffer(gl.ARRAY_BUFFER, buffer) @@ -65,14 +50,21 @@ func (s *vertexStream) newContext() *vertexStreamContext { gl.BindBuffer(gl.ARRAY_BUFFER, gl.Buffer{}) checkError() - return &vertexStreamContext{buffer} + globalStats.vertexStreamContextCount.inc() + return &vertexStreamContext{buffer: buffer} +} + +type vertexStreamContext struct { + contextResource + buffer gl.Buffer } -func (c vertexStreamContext) bind() { +func (c *vertexStreamContext) bind() { gl.BindBuffer(gl.ARRAY_BUFFER, c.buffer) } func (c *vertexStreamContext) destroy() { + globalStats.vertexStreamContextCount.dec() gl.DeleteBuffer(c.buffer) c.buffer = gl.Buffer{} } diff --git a/drivers/gl/viewport.go b/drivers/gl/viewport.go index 214384a..f5e5fe3 100644 --- a/drivers/gl/viewport.go +++ b/drivers/gl/viewport.go @@ -304,22 +304,14 @@ func (v *viewport) drawFrameUpdate(ctx *context) { func (v *viewport) SetCanvas(cc gxui.Canvas) { cnt := atomic.AddUint32(&v.redrawCount, 1) c := cc.(*canvas) - if c != nil { - c.addRef() - } v.driver.asyncDriver(func() { // Only use the canvas of the most recent SetCanvas call. v.window.MakeContextCurrent() if atomic.LoadUint32(&v.redrawCount) == cnt { - if v.canvas != nil { - v.canvas.release() - } v.canvas = c if v.canvas != nil { v.render() } - } else if c != nil { - c.release() } }) } @@ -459,10 +451,7 @@ func (v *viewport) Destroy() { v.driver.asyncDriver(func() { if !v.destroyed { v.window.MakeContextCurrent() - if v.canvas != nil { - v.canvas.Release() - v.canvas = nil - } + v.canvas = nil v.context.destroy() v.window.Destroy() v.onDestroy.Fire() diff --git a/mixins/parts/draw_paint.go b/mixins/parts/draw_paint.go index 3f5fa0a..8a791dd 100644 --- a/mixins/parts/draw_paint.go +++ b/mixins/parts/draw_paint.go @@ -38,12 +38,6 @@ func verifyDetach(o DrawPaintOuter) { func (d *DrawPaint) Init(outer DrawPaintOuter, theme gxui.Theme) { d.outer = outer d.driver = theme.Driver() - outer.OnDetach(func() { - if d.canvas != nil { - d.canvas.Release() - d.canvas = nil - } - }) if debugVerifyDetachOnGC { runtime.SetFinalizer(d.outer, verifyDetach) @@ -70,9 +64,6 @@ func (d *DrawPaint) Draw() gxui.Canvas { return nil // No area to draw in } if d.canvas == nil || d.canvas.Size() != s || d.redrawRequested { - if d.canvas != nil { - d.canvas.Release() - } d.canvas = d.driver.CreateCanvas(s) d.redrawRequested = false d.outer.Paint(d.canvas) diff --git a/mixins/window.go b/mixins/window.go index effcc36..c2b17b4 100644 --- a/mixins/window.go +++ b/mixins/window.go @@ -136,7 +136,6 @@ func (w *Window) Draw() gxui.Canvas { w.outer.Paint(c) c.Complete() w.viewport.SetCanvas(c) - c.Release() return c } else { return nil diff --git a/texture.go b/texture.go index e0b9e15..c6a9d02 100644 --- a/texture.go +++ b/texture.go @@ -5,8 +5,9 @@ package gxui import ( - "github.com/google/gxui/math" "image" + + "github.com/google/gxui/math" ) type Texture interface { @@ -15,6 +16,4 @@ type Texture interface { SizePixels() math.Size FlipY() bool SetFlipY(bool) - //AddRef() - Release() } diff --git a/themes/basic/progress_bar.go b/themes/basic/progress_bar.go index d4617ef..11c625e 100644 --- a/themes/basic/progress_bar.go +++ b/themes/basic/progress_bar.go @@ -41,7 +41,6 @@ func CreateProgressBar(theme *Theme) gxui.ProgressBar { b.OnDetach(func() { if b.chevrons != nil { - b.chevrons.Release() b.chevrons = nil b.ticker.Stop() b.ticker = nil @@ -62,10 +61,7 @@ func (b *ProgressBar) animationTick() { func (b *ProgressBar) SetSize(size math.Size) { b.ProgressBar.SetSize(size) - if b.chevrons != nil { - b.chevrons.Release() - b.chevrons = nil - } + b.chevrons = nil if size.Area() > 0 { b.chevrons = b.theme.Driver().CreateCanvas(size) b.chevronWidth = size.H / 2