gpu: fold buffer clearing into framebuffer bind

In Metal, clearing a framebuffer is most efficiently done during bind.
Modify our driver accordingly.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-08-11 15:03:45 +02:00
parent d38c78d7ac
commit 1af910959b
7 changed files with 71 additions and 61 deletions
+12 -13
View File
@@ -582,12 +582,6 @@ func (g *compute) Frame(target RenderTarget) error {
t.blit = t.t.newTimer() 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() t.compact.begin()
if err := g.compactLayers(); err != nil { if err := g.compactLayers(); err != nil {
return err return err
@@ -598,7 +592,14 @@ func (g *compute) Frame(target RenderTarget) error {
return err return err
} }
t.render.end() 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() t.blit.begin()
g.blitLayers(viewport) g.blitLayers(viewport)
t.blit.end() 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)} dr := image.Rectangle{Min: l.newPlace.pos, Max: l.newPlace.pos.Add(sz)}
g.ctx.BlitFramebuffer(dst, src, sr, dr) g.ctx.BlitFramebuffer(dst, src, sr, dr)
l.place.atlas.layers-- l.place.atlas.layers--
if l.place.atlas.layers == 0 {
l.place.atlas.fbo.Invalidate()
}
layers[i].place = l.newPlace layers[i].place = l.newPlace
} }
layers = layers[end:] layers = layers[end:]
@@ -926,11 +924,12 @@ restart:
m.buffer.ensureCapacity(false, g.ctx, driver.BufferBindingVertices, n) m.buffer.ensureCapacity(false, g.ctx, driver.BufferBindingVertices, n)
m.buffer.buffer.Upload(vertexData) m.buffer.buffer.Upload(vertexData)
g.ctx.BindTexture(0, g.images.tex) g.ctx.BindTexture(0, g.images.tex)
g.ctx.BindFramebuffer(m.fbo) var d driver.LoadDesc
g.ctx.Viewport(0, 0, texSize, texSize)
if reclaimed { 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.BindPipeline(m.pipeline)
g.ctx.BindVertexBuffer(m.buffer.buffer, int(unsafe.Sizeof(m.quads[0])), 0) g.ctx.BindVertexBuffer(m.buffer.buffer, int(unsafe.Sizeof(m.quads[0])), 0)
g.ctx.DrawArrays(driver.DrawModeTriangles, 0, len(m.quads)) g.ctx.DrawArrays(driver.DrawModeTriangles, 0, len(m.quads))
+12 -12
View File
@@ -445,11 +445,6 @@ func (g *gpu) Frame(target RenderTarget) error {
for _, img := range g.drawOps.imageOps { for _, img := range g.drawOps.imageOps {
expandPathOp(img.path, img.clip) 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.ctx.Viewport(0, 0, viewport.X, viewport.Y)
g.stencilTimer.begin() g.stencilTimer.begin()
g.renderer.packStencils(&g.drawOps.pathOps) g.renderer.packStencils(&g.drawOps.pathOps)
@@ -458,12 +453,17 @@ func (g *gpu) Frame(target RenderTarget) error {
g.renderer.intersect(g.drawOps.imageOps) g.renderer.intersect(g.drawOps.imageOps)
g.stencilTimer.end() g.stencilTimer.end()
g.coverTimer.begin() 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.ctx.Viewport(0, 0, viewport.X, viewport.Y)
g.renderer.drawOps(g.cache, g.drawOps.imageOps) g.renderer.drawOps(g.cache, g.drawOps.imageOps)
g.renderer.pather.stenciler.invalidateFBO()
g.coverTimer.end() g.coverTimer.end()
g.ctx.BindFramebuffer(defFBO)
g.cleanupTimer.begin() g.cleanupTimer.begin()
g.cache.frame() g.cache.frame()
g.drawOps.pathCache.frame() g.drawOps.pathCache.frame()
@@ -689,8 +689,7 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
if fbo != p.place.Idx { if fbo != p.place.Idx {
fbo = p.place.Idx fbo = p.place.Idx
f := r.pather.stenciler.cover(fbo) f := r.pather.stenciler.cover(fbo)
r.ctx.BindFramebuffer(f.fbo) r.ctx.BindFramebuffer(f.fbo, driver.LoadDesc{Action: driver.LoadActionClear})
r.ctx.Clear(0.0, 0.0, 0.0, 0.0)
} }
v, _ := pathCache.get(p.pathKey) v, _ := pathCache.get(p.pathKey)
r.pather.stencilPath(p.clip, p.off, p.place.Pos, v.data) 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 { if fbo != img.place.Idx {
fbo = img.place.Idx fbo = img.place.Idx
f := r.pather.stenciler.intersections.fbos[fbo] f := r.pather.stenciler.intersections.fbos[fbo]
r.ctx.BindFramebuffer(f.fbo) d := driver.LoadDesc{Action: driver.LoadActionClear}
r.ctx.Clear(1.0, 0.0, 0.0, 0.0) 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.ctx.Viewport(img.place.Pos.X, img.place.Pos.Y, img.clip.Dx(), img.clip.Dy())
r.intersectPath(img.path, img.clip) r.intersectPath(img.path, img.clip)
+10 -6
View File
@@ -141,10 +141,12 @@ func TestFramebuffers(t *testing.T) {
col2 = color.NRGBA{R: 0xfe, G: 0xba, B: 0xbe, A: 0xca} col2 = color.NRGBA{R: 0xfe, G: 0xba, B: 0xbe, A: 0xca}
) )
fcol1, fcol2 := f32color.LinearFromSRGB(col1), f32color.LinearFromSRGB(col2) fcol1, fcol2 := f32color.LinearFromSRGB(col1), f32color.LinearFromSRGB(col2)
b.BindFramebuffer(fbo1) d := driver.LoadDesc{Action: driver.LoadActionClear}
b.Clear(fcol1.Float32()) c := &d.ClearColor
b.BindFramebuffer(fbo2) c.R, c.G, c.B, c.A = fcol1.Float32()
b.Clear(fcol2.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) img := screenshot(t, b, fbo1, sz)
if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col1) { if got := img.RGBAAt(0, 0); got != f32color.NRGBAToRGBA(col1) {
t.Errorf("got color %v, expected %v", 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 { func setupFBO(t *testing.T, b driver.Device, size image.Point) driver.Framebuffer {
fbo := newFBO(t, b, size) fbo := newFBO(t, b, size)
b.BindFramebuffer(fbo)
// ClearColor accepts linear RGBA colors, while 8-bit colors // ClearColor accepts linear RGBA colors, while 8-bit colors
// are in the sRGB color space. // are in the sRGB color space.
col := f32color.LinearFromSRGB(clearCol) 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) b.Viewport(0, 0, size.X, size.Y)
return fbo return fbo
} }
+6 -6
View File
@@ -432,11 +432,6 @@ func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader,
return &FragmentShader{b, fs}, nil 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) { func (b *Backend) Viewport(x, y, width, height int) {
b.viewport = d3d11.VIEWPORT{ b.viewport = d3d11.VIEWPORT{
TopLeftX: float32(x), TopLeftX: float32(x),
@@ -643,9 +638,14 @@ func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
return nil return nil
} }
func (b *Backend) BindFramebuffer(fbo driver.Framebuffer) { func (b *Backend) BindFramebuffer(fbo driver.Framebuffer, d driver.LoadDesc) {
b.fbo = fbo.(*Framebuffer) b.fbo = fbo.(*Framebuffer)
b.ctx.OMSetRenderTargets(b.fbo.renderTarget, nil) 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() { func (f *Framebuffer) Invalidate() {
+19 -3
View File
@@ -30,14 +30,13 @@ type Device interface {
NewFragmentShader(src shader.Sources) (FragmentShader, error) NewFragmentShader(src shader.Sources) (FragmentShader, error)
NewPipeline(desc PipelineDesc) (Pipeline, error) NewPipeline(desc PipelineDesc) (Pipeline, error)
Clear(r, g, b, a float32)
Viewport(x, y, width, height int) Viewport(x, y, width, height int)
DrawArrays(mode DrawMode, off, count int) DrawArrays(mode DrawMode, off, count int)
DrawElements(mode DrawMode, off, count int) DrawElements(mode DrawMode, off, count int)
BindProgram(p Program) BindProgram(p Program)
BindPipeline(p Pipeline) BindPipeline(p Pipeline)
BindFramebuffer(f Framebuffer) BindFramebuffer(f Framebuffer, a LoadDesc)
BindTexture(unit int, t Texture) BindTexture(unit int, t Texture)
BindVertexBuffer(b Buffer, stride, offset int) BindVertexBuffer(b Buffer, stride, offset int)
BindIndexBuffer(b Buffer) BindIndexBuffer(b Buffer)
@@ -53,6 +52,16 @@ type Device interface {
Release() Release()
} }
type LoadDesc struct {
Action LoadAction
ClearColor struct {
R float32
G float32
B float32
A float32
}
}
type Pipeline interface { type Pipeline interface {
Release() Release()
} }
@@ -89,6 +98,8 @@ type TextureFormat uint8
type BufferBinding uint8 type BufferBinding uint8
type LoadAction uint8
type Features uint type Features uint
type Caps struct { type Caps struct {
@@ -119,7 +130,6 @@ type Buffer interface {
type Framebuffer interface { type Framebuffer interface {
RenderTarget RenderTarget
Invalidate()
Release() Release()
ReadPixels(src image.Rectangle, pixels []byte) error ReadPixels(src image.Rectangle, pixels []byte) error
} }
@@ -182,6 +192,12 @@ const (
BlendFactorDstColor BlendFactorDstColor
) )
const (
LoadActionKeep LoadAction = iota
LoadActionClear
LoadActionInvalidate
)
var ErrContentLost = errors.New("buffer content lost") var ErrContentLost = errors.New("buffer content lost")
func (f Features) Has(feats Features) bool { func (f Features) Has(feats Features) bool {
+11 -9
View File
@@ -268,7 +268,7 @@ func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport im
} }
b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO) b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, renderFBO)
if b.sRGBFBO != nil && !clear { 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} 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) gltex := tex.(*texture)
fb := b.funcs.CreateFramebuffer() fb := b.funcs.CreateFramebuffer()
fbo := &framebuffer{backend: b, obj: fb} fbo := &framebuffer{backend: b, obj: fb}
b.BindFramebuffer(fbo) b.BindFramebuffer(fbo, driver.LoadDesc{})
if err := glErr(b.funcs); err != nil { if err := glErr(b.funcs); err != nil {
fbo.Release() fbo.Release()
return nil, err 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) 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.glstate.setClearColor(b.funcs, colR, colG, colB, colA)
b.funcs.Clear(gl.COLOR_BUFFER_BIT) 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 { func (f *framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
glErr(f.backend.funcs) glErr(f.backend.funcs)
f.backend.BindFramebuffer(f) f.backend.BindFramebuffer(f, driver.LoadDesc{})
if len(pixels) < src.Dx()*src.Dy()*4 { if len(pixels) < src.Dx()*src.Dy()*4 {
return errors.New("unexpected RGBA size") 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) 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) b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo.(*framebuffer).obj)
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) Invalidate() {
f.backend.BindFramebuffer(f)
f.backend.funcs.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
} }
func (f *framebuffer) Release() { func (f *framebuffer) Release() {
-11
View File
@@ -284,12 +284,6 @@ func (s *fboSet) resize(ctx driver.Device, sizes []image.Point) {
s.delete(ctx, len(sizes)) 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) { func (s *fboSet) delete(ctx driver.Device, idx int) {
for i := idx; i < len(s.fbos); i++ { for i := idx; i < len(s.fbos); i++ {
f := 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) 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 { func (s *stenciler) cover(idx int) stencilFBO {
return s.fbos.fbos[idx] return s.fbos.fbos[idx]
} }