diff --git a/gpu/compute.go b/gpu/compute.go index e65e1005..0dd3c9b2 100644 --- a/gpu/compute.go +++ b/gpu/compute.go @@ -582,12 +582,6 @@ func (g *compute) Frame(target RenderTarget) error { t.blit = t.t.newTimer() } - g.ctx.BindFramebuffer(defFBO) - if g.collector.clear { - g.collector.clear = false - g.ctx.Clear(g.collector.clearColor.Float32()) - } - t.compact.begin() if err := g.compactLayers(); err != nil { return err @@ -598,7 +592,14 @@ func (g *compute) Frame(target RenderTarget) error { return err } t.render.end() - g.ctx.BindFramebuffer(defFBO) + var d driver.LoadDesc + if g.collector.clear { + g.collector.clear = false + d.Action = driver.LoadActionClear + c := &d.ClearColor + c.R, c.G, c.B, c.A = g.collector.clearColor.Float32() + } + g.ctx.BindFramebuffer(defFBO, d) t.blit.begin() g.blitLayers(viewport) t.blit.end() @@ -691,9 +692,6 @@ func (g *compute) compactLayers() error { dr := image.Rectangle{Min: l.newPlace.pos, Max: l.newPlace.pos.Add(sz)} g.ctx.BlitFramebuffer(dst, src, sr, dr) l.place.atlas.layers-- - if l.place.atlas.layers == 0 { - l.place.atlas.fbo.Invalidate() - } layers[i].place = l.newPlace } layers = layers[end:] @@ -926,11 +924,12 @@ restart: m.buffer.ensureCapacity(false, g.ctx, driver.BufferBindingVertices, n) m.buffer.buffer.Upload(vertexData) g.ctx.BindTexture(0, g.images.tex) - g.ctx.BindFramebuffer(m.fbo) - g.ctx.Viewport(0, 0, texSize, texSize) + var d driver.LoadDesc if reclaimed { - g.ctx.Clear(0, 0, 0, 0) + d.Action = driver.LoadActionClear } + g.ctx.BindFramebuffer(m.fbo, d) + g.ctx.Viewport(0, 0, texSize, texSize) g.ctx.BindPipeline(m.pipeline) g.ctx.BindVertexBuffer(m.buffer.buffer, int(unsafe.Sizeof(m.quads[0])), 0) g.ctx.DrawArrays(driver.DrawModeTriangles, 0, len(m.quads)) diff --git a/gpu/gpu.go b/gpu/gpu.go index fc6f8188..acdb9f69 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -445,11 +445,6 @@ func (g *gpu) Frame(target RenderTarget) error { for _, img := range g.drawOps.imageOps { expandPathOp(img.path, img.clip) } - g.ctx.BindFramebuffer(defFBO) - if g.drawOps.clear { - g.drawOps.clear = false - g.ctx.Clear(g.drawOps.clearColor.Float32()) - } g.ctx.Viewport(0, 0, viewport.X, viewport.Y) g.stencilTimer.begin() g.renderer.packStencils(&g.drawOps.pathOps) @@ -458,12 +453,17 @@ func (g *gpu) Frame(target RenderTarget) error { g.renderer.intersect(g.drawOps.imageOps) g.stencilTimer.end() g.coverTimer.begin() - g.ctx.BindFramebuffer(defFBO) + var d driver.LoadDesc + if g.drawOps.clear { + g.drawOps.clear = false + d.Action = driver.LoadActionClear + c := &d.ClearColor + c.R, c.G, c.B, c.A = g.drawOps.clearColor.Float32() + } + g.ctx.BindFramebuffer(defFBO, d) g.ctx.Viewport(0, 0, viewport.X, viewport.Y) g.renderer.drawOps(g.cache, g.drawOps.imageOps) - g.renderer.pather.stenciler.invalidateFBO() g.coverTimer.end() - g.ctx.BindFramebuffer(defFBO) g.cleanupTimer.begin() g.cache.frame() g.drawOps.pathCache.frame() @@ -689,8 +689,7 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) { if fbo != p.place.Idx { fbo = p.place.Idx f := r.pather.stenciler.cover(fbo) - r.ctx.BindFramebuffer(f.fbo) - r.ctx.Clear(0.0, 0.0, 0.0, 0.0) + r.ctx.BindFramebuffer(f.fbo, driver.LoadDesc{Action: driver.LoadActionClear}) } v, _ := pathCache.get(p.pathKey) r.pather.stencilPath(p.clip, p.off, p.place.Pos, v.data) @@ -711,8 +710,9 @@ func (r *renderer) intersect(ops []imageOp) { if fbo != img.place.Idx { fbo = img.place.Idx f := r.pather.stenciler.intersections.fbos[fbo] - r.ctx.BindFramebuffer(f.fbo) - r.ctx.Clear(1.0, 0.0, 0.0, 0.0) + d := driver.LoadDesc{Action: driver.LoadActionClear} + d.ClearColor.R = 1.0 + r.ctx.BindFramebuffer(f.fbo, d) } r.ctx.Viewport(img.place.Pos.X, img.place.Pos.Y, img.clip.Dx(), img.clip.Dy()) r.intersectPath(img.path, img.clip) diff --git a/gpu/headless/driver_test.go b/gpu/headless/driver_test.go index fb646942..acad56e1 100644 --- a/gpu/headless/driver_test.go +++ b/gpu/headless/driver_test.go @@ -141,10 +141,12 @@ func TestFramebuffers(t *testing.T) { col2 = color.NRGBA{R: 0xfe, G: 0xba, B: 0xbe, A: 0xca} ) fcol1, fcol2 := f32color.LinearFromSRGB(col1), f32color.LinearFromSRGB(col2) - b.BindFramebuffer(fbo1) - b.Clear(fcol1.Float32()) - b.BindFramebuffer(fbo2) - b.Clear(fcol2.Float32()) + d := driver.LoadDesc{Action: driver.LoadActionClear} + c := &d.ClearColor + c.R, c.G, c.B, c.A = fcol1.Float32() + b.BindFramebuffer(fbo1, d) + c.R, c.G, c.B, c.A = fcol2.Float32() + b.BindFramebuffer(fbo2, d) img := screenshot(t, b, fbo1, sz) if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col1) { t.Errorf("got color %v, expected %v", got, f32color.NRGBAToRGBA(col1)) @@ -157,11 +159,13 @@ func TestFramebuffers(t *testing.T) { func setupFBO(t *testing.T, b driver.Device, size image.Point) driver.Framebuffer { fbo := newFBO(t, b, size) - b.BindFramebuffer(fbo) // ClearColor accepts linear RGBA colors, while 8-bit colors // are in the sRGB color space. col := f32color.LinearFromSRGB(clearCol) - b.Clear(col.Float32()) + d := driver.LoadDesc{Action: driver.LoadActionClear} + c := &d.ClearColor + c.R, c.G, c.B, c.A = col.Float32() + b.BindFramebuffer(fbo, d) b.Viewport(0, 0, size.X, size.Y) return fbo } diff --git a/gpu/internal/d3d11/d3d11_windows.go b/gpu/internal/d3d11/d3d11_windows.go index 3a0bb4bb..499d3681 100644 --- a/gpu/internal/d3d11/d3d11_windows.go +++ b/gpu/internal/d3d11/d3d11_windows.go @@ -432,11 +432,6 @@ func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, return &FragmentShader{b, fs}, nil } -func (b *Backend) Clear(colr, colg, colb, cola float32) { - b.clearColor = [4]float32{colr, colg, colb, cola} - b.ctx.ClearRenderTargetView(b.fbo.renderTarget, &b.clearColor) -} - func (b *Backend) Viewport(x, y, width, height int) { b.viewport = d3d11.VIEWPORT{ TopLeftX: float32(x), @@ -643,9 +638,14 @@ func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error { return nil } -func (b *Backend) BindFramebuffer(fbo driver.Framebuffer) { +func (b *Backend) BindFramebuffer(fbo driver.Framebuffer, d driver.LoadDesc) { b.fbo = fbo.(*Framebuffer) b.ctx.OMSetRenderTargets(b.fbo.renderTarget, nil) + if d.Action == driver.LoadActionClear { + c := d.ClearColor + b.clearColor = [4]float32{c.R, c.G, c.B, c.A} + b.ctx.ClearRenderTargetView(b.fbo.renderTarget, &b.clearColor) + } } func (f *Framebuffer) Invalidate() { diff --git a/gpu/internal/driver/driver.go b/gpu/internal/driver/driver.go index 719f39f2..f10cef54 100644 --- a/gpu/internal/driver/driver.go +++ b/gpu/internal/driver/driver.go @@ -30,14 +30,13 @@ type Device interface { NewFragmentShader(src shader.Sources) (FragmentShader, error) NewPipeline(desc PipelineDesc) (Pipeline, error) - Clear(r, g, b, a float32) Viewport(x, y, width, height int) DrawArrays(mode DrawMode, off, count int) DrawElements(mode DrawMode, off, count int) BindProgram(p Program) BindPipeline(p Pipeline) - BindFramebuffer(f Framebuffer) + BindFramebuffer(f Framebuffer, a LoadDesc) BindTexture(unit int, t Texture) BindVertexBuffer(b Buffer, stride, offset int) BindIndexBuffer(b Buffer) @@ -53,6 +52,16 @@ type Device interface { Release() } +type LoadDesc struct { + Action LoadAction + ClearColor struct { + R float32 + G float32 + B float32 + A float32 + } +} + type Pipeline interface { Release() } @@ -89,6 +98,8 @@ type TextureFormat uint8 type BufferBinding uint8 +type LoadAction uint8 + type Features uint type Caps struct { @@ -119,7 +130,6 @@ type Buffer interface { type Framebuffer interface { RenderTarget - Invalidate() Release() ReadPixels(src image.Rectangle, pixels []byte) error } @@ -182,6 +192,12 @@ const ( BlendFactorDstColor ) +const ( + LoadActionKeep LoadAction = iota + LoadActionClear + LoadActionInvalidate +) + var ErrContentLost = errors.New("buffer content lost") func (f Features) Has(feats Features) bool { diff --git a/gpu/internal/opengl/opengl.go b/gpu/internal/opengl/opengl.go index 4a5be8eb..21cb3d4b 100644 --- a/gpu/internal/opengl/opengl.go +++ b/gpu/internal/opengl/opengl.go @@ -268,7 +268,7 @@ func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport im } b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO) if b.sRGBFBO != nil && !clear { - b.Clear(0, 0, 0, 0) + b.clearOutput(0, 0, 0, 0) } return &framebuffer{backend: b, obj: renderFBO, foreign: true} } @@ -652,7 +652,7 @@ func (b *Backend) NewFramebuffer(tex driver.Texture) (driver.Framebuffer, error) gltex := tex.(*texture) fb := b.funcs.CreateFramebuffer() fbo := &framebuffer{backend: b, obj: fb} - b.BindFramebuffer(fbo) + b.BindFramebuffer(fbo, driver.LoadDesc{}) if err := glErr(b.funcs); err != nil { fbo.Release() return nil, err @@ -849,7 +849,7 @@ func (b *Backend) Viewport(x, y, width, height int) { b.glstate.setViewport(b.funcs, x, y, width, height) } -func (b *Backend) Clear(colR, colG, colB, colA float32) { +func (b *Backend) clearOutput(colR, colG, colB, colA float32) { b.glstate.setClearColor(b.funcs, colR, colG, colB, colA) b.funcs.Clear(gl.COLOR_BUFFER_BIT) } @@ -1179,7 +1179,7 @@ func (b *Backend) BlitFramebuffer(dst, src driver.Framebuffer, srect, drect imag func (f *framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error { glErr(f.backend.funcs) - f.backend.BindFramebuffer(f) + f.backend.BindFramebuffer(f, driver.LoadDesc{}) if len(pixels) < src.Dx()*src.Dy()*4 { return errors.New("unexpected RGBA size") } @@ -1195,13 +1195,15 @@ func (b *Backend) BindPipeline(pl driver.Pipeline) { b.BlendFunc(p.blend.SrcFactor, p.blend.DstFactor) } -func (b *Backend) BindFramebuffer(fbo driver.Framebuffer) { +func (b *Backend) BindFramebuffer(fbo driver.Framebuffer, desc driver.LoadDesc) { b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo.(*framebuffer).obj) -} - -func (f *framebuffer) Invalidate() { - f.backend.BindFramebuffer(f) - f.backend.funcs.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0) + switch desc.Action { + case driver.LoadActionClear: + c := desc.ClearColor + b.clearOutput(c.R, c.G, c.B, c.A) + case driver.LoadActionInvalidate: + b.funcs.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0) + } } func (f *framebuffer) Release() { diff --git a/gpu/path.go b/gpu/path.go index cc6adb04..a78692d7 100644 --- a/gpu/path.go +++ b/gpu/path.go @@ -284,12 +284,6 @@ func (s *fboSet) resize(ctx driver.Device, sizes []image.Point) { s.delete(ctx, len(sizes)) } -func (s *fboSet) invalidate(ctx driver.Device) { - for _, f := range s.fbos { - f.fbo.Invalidate() - } -} - func (s *fboSet) delete(ctx driver.Device, idx int) { for i := idx; i < len(s.fbos); i++ { f := s.fbos[i] @@ -348,11 +342,6 @@ func (s *stenciler) beginIntersect(sizes []image.Point) { s.ctx.BindPipeline(s.ipipeline.pipeline.pipeline) } -func (s *stenciler) invalidateFBO() { - s.intersections.invalidate(s.ctx) - s.fbos.invalidate(s.ctx) -} - func (s *stenciler) cover(idx int) stencilFBO { return s.fbos.fbos[idx] }