gpu: fold driver.Framebuffer functionality into driver.Texture

driver.Device.NewFramebuffer doesn't provide additional information over
driver.Device.NewTexture, so Texture can hold its (optional) framebuffer
on behalf of the renderers. Metal don't even need a separate framebuffer
object.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-09-19 19:36:48 +02:00
parent b599a6d1c5
commit 0bdd24c51e
9 changed files with 135 additions and 204 deletions
+7 -18
View File
@@ -149,7 +149,6 @@ type atlasMove struct {
type textureAtlas struct { type textureAtlas struct {
image driver.Texture image driver.Texture
fbo driver.Framebuffer
format driver.TextureFormat format driver.TextureFormat
bindings driver.BufferBinding bindings driver.BufferBinding
hasCPU bool hasCPU bool
@@ -356,7 +355,7 @@ type memoryHeader struct {
type rectangle [4]f32.Point type rectangle [4]f32.Point
const ( const (
layersBindings = driver.BufferBindingShaderStorageWrite | driver.BufferBindingTexture | driver.BufferBindingFramebuffer layersBindings = driver.BufferBindingShaderStorageWrite | driver.BufferBindingTexture
materialsBindings = driver.BufferBindingFramebuffer | driver.BufferBindingShaderStorageRead materialsBindings = driver.BufferBindingFramebuffer | driver.BufferBindingShaderStorageRead
// Materials and layers can share texture storage if their bindings match. // Materials and layers can share texture storage if their bindings match.
combinedBindings = layersBindings | materialsBindings combinedBindings = layersBindings | materialsBindings
@@ -644,7 +643,7 @@ func (g *compute) frame(target RenderTarget) error {
func (g *compute) dumpAtlases() { func (g *compute) dumpAtlases() {
for i, a := range g.atlases { for i, a := range g.atlases {
dump, err := driver.DownloadImage(g.ctx, a.fbo, image.Rectangle{Max: a.size}) dump, err := driver.DownloadImage(g.ctx, a.image, image.Rectangle{Max: a.size})
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -752,7 +751,7 @@ func (g *compute) compactAllocs() error {
} }
for _, move := range g.moves { for _, move := range g.moves {
if !move.cpu { if !move.cpu {
g.ctx.CopyTexture(dstAtlas.image, move.dstPos, move.src.fbo, move.srcRect) g.ctx.CopyTexture(dstAtlas.image, move.dstPos, move.src.image, move.srcRect)
} else { } else {
src := move.src.cpuImage.Data() src := move.src.cpuImage.Data()
dst := dstAtlas.cpuImage.Data() dst := dstAtlas.cpuImage.Data()
@@ -848,7 +847,7 @@ func (g *compute) renderLayers(viewport image.Point) error {
return nil return nil
} }
func (g *compute) blitLayers(d driver.LoadDesc, fbo driver.Framebuffer, viewport image.Point) { func (g *compute) blitLayers(d driver.LoadDesc, fbo driver.Texture, viewport image.Point) {
layers := g.collector.frame.layers layers := g.collector.frame.layers
g.output.layerVertices = g.output.layerVertices[:0] g.output.layerVertices = g.output.layerVertices[:0]
for _, l := range layers { for _, l := range layers {
@@ -1008,7 +1007,7 @@ func (g *compute) renderMaterials() error {
d.Action = driver.LoadActionClear d.Action = driver.LoadActionClear
} }
g.ctx.PrepareTexture(imgAtlas.image) g.ctx.PrepareTexture(imgAtlas.image)
g.ctx.BeginRenderPass(atlas.fbo, d) g.ctx.BeginRenderPass(atlas.image, d)
g.ctx.BindTexture(0, imgAtlas.image) g.ctx.BindTexture(0, imgAtlas.image)
g.ctx.BindPipeline(m.pipeline) g.ctx.BindPipeline(m.pipeline)
g.ctx.BindUniforms(m.uniforms.buf) g.ctx.BindUniforms(m.uniforms.buf)
@@ -1023,14 +1022,14 @@ func (g *compute) renderMaterials() error {
if !g.useCPU { if !g.useCPU {
continue continue
} }
copyFBO := atlas.fbo src := atlas.image
data := atlas.cpuImage.Data() data := atlas.cpuImage.Data()
for _, a := range newAllocs { for _, a := range newAllocs {
stride := atlas.size.X * 4 stride := atlas.size.X * 4
col := a.rect.Min.X * 4 col := a.rect.Min.X * 4
row := stride * a.rect.Min.Y row := stride * a.rect.Min.Y
off := col + row off := col + row
copyFBO.ReadPixels(a.rect, data[off:], stride) src.ReadPixels(a.rect, data[off:], stride)
} }
} }
return nil return nil
@@ -1458,12 +1457,6 @@ func (a *textureAtlas) resize(ctx driver.Device, size image.Point) error {
if err != nil { if err != nil {
return err return err
} }
fbo, err := ctx.NewFramebuffer(img)
if err != nil {
img.Release()
return err
}
a.fbo = fbo
a.image = img a.image = img
a.size = size a.size = size
return nil return nil
@@ -1518,10 +1511,6 @@ func (g *compute) Release() {
} }
func (a *textureAtlas) Release() { func (a *textureAtlas) Release() {
if a.fbo != nil {
a.fbo.Release()
a.fbo = nil
}
if a.image != nil { if a.image != nil {
a.image.Release() a.image.Release()
a.image = nil a.image = nil
+2 -2
View File
@@ -657,7 +657,7 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
} }
fbo = p.place.Idx fbo = p.place.Idx
f := r.pather.stenciler.cover(fbo) f := r.pather.stenciler.cover(fbo)
r.ctx.BeginRenderPass(f.fbo, driver.LoadDesc{Action: driver.LoadActionClear}) r.ctx.BeginRenderPass(f.tex, driver.LoadDesc{Action: driver.LoadActionClear})
r.ctx.BindPipeline(r.pather.stenciler.pipeline.pipeline.pipeline) r.ctx.BindPipeline(r.pather.stenciler.pipeline.pipeline.pipeline)
r.ctx.BindIndexBuffer(r.pather.stenciler.indexBuf) r.ctx.BindIndexBuffer(r.pather.stenciler.indexBuf)
} }
@@ -697,7 +697,7 @@ func (r *renderer) intersect(ops []imageOp) {
f := r.pather.stenciler.intersections.fbos[fbo] f := r.pather.stenciler.intersections.fbos[fbo]
d := driver.LoadDesc{Action: driver.LoadActionClear} d := driver.LoadDesc{Action: driver.LoadActionClear}
d.ClearColor.R = 1.0 d.ClearColor.R = 1.0
r.ctx.BeginRenderPass(f.fbo, d) r.ctx.BeginRenderPass(f.tex, d)
r.ctx.BindPipeline(r.pather.stenciler.ipipeline.pipeline.pipeline) r.ctx.BindPipeline(r.pather.stenciler.ipipeline.pipeline.pipeline)
r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0) r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0)
} }
+3 -10
View File
@@ -144,7 +144,7 @@ func TestFramebuffers(t *testing.T) {
} }
} }
func newFBO(t *testing.T, b driver.Device, size image.Point) driver.Framebuffer { func newFBO(t *testing.T, b driver.Device, size image.Point) driver.Texture {
fboTex, err := b.NewTexture( fboTex, err := b.NewTexture(
driver.TextureFormatSRGBA, driver.TextureFormatSRGBA,
size.X, size.Y, size.X, size.Y,
@@ -157,14 +157,7 @@ func newFBO(t *testing.T, b driver.Device, size image.Point) driver.Framebuffer
t.Cleanup(func() { t.Cleanup(func() {
fboTex.Release() fboTex.Release()
}) })
fbo, err := b.NewFramebuffer(fboTex) return fboTex
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
fbo.Release()
})
return fbo
} }
func newDriver(t *testing.T) driver.Device { func newDriver(t *testing.T) driver.Device {
@@ -191,7 +184,7 @@ func newDriver(t *testing.T) driver.Device {
return b return b
} }
func screenshot(t *testing.T, d driver.Device, fbo driver.Framebuffer, size image.Point) *image.RGBA { func screenshot(t *testing.T, d driver.Device, fbo driver.Texture, size image.Point) *image.RGBA {
img, err := driver.DownloadImage(d, fbo, image.Rectangle{Max: size}) img, err := driver.DownloadImage(d, fbo, image.Rectangle{Max: size})
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
+2 -14
View File
@@ -21,7 +21,6 @@ type Window struct {
dev driver.Device dev driver.Device
gpu gpu.GPU gpu gpu.GPU
fboTex driver.Texture fboTex driver.Texture
fbo driver.Framebuffer
} }
type context interface { type context interface {
@@ -56,20 +55,13 @@ func NewWindow(width, height int) (*Window, error) {
if err != nil { if err != nil {
return nil return nil
} }
fbo, err := dev.NewFramebuffer(fboTex)
if err != nil {
fboTex.Release()
return err
}
gp, err := gpu.New(api) gp, err := gpu.New(api)
if err != nil { if err != nil {
fbo.Release()
fboTex.Release() fboTex.Release()
dev.Release() dev.Release()
return err return err
} }
w.fboTex = fboTex w.fboTex = fboTex
w.fbo = fbo
w.gpu = gp w.gpu = gp
w.dev = dev w.dev = dev
return err return err
@@ -84,10 +76,6 @@ func NewWindow(width, height int) (*Window, error) {
// Release resources associated with the window. // Release resources associated with the window.
func (w *Window) Release() { func (w *Window) Release() {
contextDo(w.ctx, func() error { contextDo(w.ctx, func() error {
if w.fbo != nil {
w.fbo.Release()
w.fbo = nil
}
if w.fboTex != nil { if w.fboTex != nil {
w.fboTex.Release() w.fboTex.Release()
w.fboTex = nil w.fboTex = nil
@@ -113,7 +101,7 @@ func (w *Window) Release() {
func (w *Window) Frame(frame *op.Ops) error { func (w *Window) Frame(frame *op.Ops) error {
return contextDo(w.ctx, func() error { return contextDo(w.ctx, func() error {
w.gpu.Clear(color.NRGBA{}) w.gpu.Clear(color.NRGBA{})
return w.gpu.Frame(frame, driver.RenderTarget(w.fbo), w.size) return w.gpu.Frame(frame, w.fboTex, w.size)
}) })
} }
@@ -122,7 +110,7 @@ func (w *Window) Screenshot() (*image.RGBA, error) {
var img *image.RGBA var img *image.RGBA
err := contextDo(w.ctx, func() error { err := contextDo(w.ctx, func() error {
var err error var err error
img, err = driver.DownloadImage(w.dev, w.fbo, image.Rectangle{Max: w.size}) img, err = driver.DownloadImage(w.dev, w.fboTex, image.Rectangle{Max: w.size})
return err return err
}) })
if err != nil { if err != nil {
+53 -65
View File
@@ -35,9 +35,6 @@ type Backend struct {
caps driver.Caps caps driver.Caps
// fbo is the currently bound fbo.
fbo *Framebuffer
floatFormat uint32 floatFormat uint32
} }
@@ -51,15 +48,18 @@ type Pipeline struct {
} }
type Texture struct { type Texture struct {
backend *Backend backend *Backend
format uint32 format uint32
bindings driver.BufferBinding bindings driver.BufferBinding
tex *d3d11.Texture2D tex *d3d11.Texture2D
sampler *d3d11.SamplerState sampler *d3d11.SamplerState
resView *d3d11.ShaderResourceView resView *d3d11.ShaderResourceView
uaView *d3d11.UnorderedAccessView uaView *d3d11.UnorderedAccessView
width int renderTarget *d3d11.RenderTargetView
height int
width int
height int
foreign bool
} }
type VertexShader struct { type VertexShader struct {
@@ -78,15 +78,6 @@ type Program struct {
shader *d3d11.ComputeShader shader *d3d11.ComputeShader
} }
type Framebuffer struct {
dev *d3d11.Device
ctx *d3d11.DeviceContext
format uint32
resource *d3d11.Resource
renderTarget *d3d11.RenderTargetView
foreign bool
}
type Buffer struct { type Buffer struct {
backend *Backend backend *Backend
bind uint32 bind uint32
@@ -159,7 +150,7 @@ func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) {
return b, nil return b, nil
} }
func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Framebuffer { func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
var ( var (
renderTarget *d3d11.RenderTargetView renderTarget *d3d11.RenderTargetView
) )
@@ -167,19 +158,19 @@ func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport im
switch t := target.(type) { switch t := target.(type) {
case driver.Direct3D11RenderTarget: case driver.Direct3D11RenderTarget:
renderTarget = (*d3d11.RenderTargetView)(t.RenderTarget) renderTarget = (*d3d11.RenderTargetView)(t.RenderTarget)
case *Framebuffer: case *Texture:
renderTarget = t.renderTarget renderTarget = t.renderTarget
default: default:
panic(fmt.Errorf("d3d11: invalid render target type: %T", target)) panic(fmt.Errorf("d3d11: invalid render target type: %T", target))
} }
} }
b.ctx.OMSetRenderTargets(renderTarget, nil) b.ctx.OMSetRenderTargets(renderTarget, nil)
return &Framebuffer{ctx: b.ctx, dev: b.dev, renderTarget: renderTarget, foreign: true} return &Texture{backend: b, renderTarget: renderTarget, foreign: true}
} }
func (b *Backend) CopyTexture(dstTex driver.Texture, dstOrigin image.Point, srcFBO driver.Framebuffer, srcRect image.Rectangle) { func (b *Backend) CopyTexture(dstTex driver.Texture, dstOrigin image.Point, srcTex driver.Texture, srcRect image.Rectangle) {
dst := (*d3d11.Resource)(unsafe.Pointer(dstTex.(*Texture).tex)) dst := (*d3d11.Resource)(unsafe.Pointer(dstTex.(*Texture).tex))
src := srcFBO.(*Framebuffer).resource src := (*d3d11.Resource)(srcTex.(*Texture).tex)
b.ctx.CopySubresourceRegion( b.ctx.CopySubresourceRegion(
dst, dst,
0, // Destination subresource. 0, // Destination subresource.
@@ -248,6 +239,7 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
sampler *d3d11.SamplerState sampler *d3d11.SamplerState
resView *d3d11.ShaderResourceView resView *d3d11.ShaderResourceView
uaView *d3d11.UnorderedAccessView uaView *d3d11.UnorderedAccessView
fbo *d3d11.RenderTargetView
) )
if bindings&driver.BufferBindingTexture != 0 { if bindings&driver.BufferBindingTexture != 0 {
var filter uint32 var filter uint32
@@ -317,21 +309,24 @@ func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, min
return nil, err return nil, err
} }
} }
return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, uaView: uaView, bindings: bindings, width: width, height: height}, nil if bindings&driver.BufferBindingFramebuffer != 0 {
} resource := (*d3d11.Resource)(unsafe.Pointer(tex))
fbo, err = b.dev.CreateRenderTargetView(resource)
func (b *Backend) NewFramebuffer(tex driver.Texture) (driver.Framebuffer, error) { if err != nil {
d3dtex := tex.(*Texture) if uaView != nil {
if d3dtex.bindings&driver.BufferBindingFramebuffer == 0 { d3d11.IUnknownRelease(unsafe.Pointer(uaView), uaView.Vtbl.Release)
return nil, errors.New("the texture was created without BufferBindingFramebuffer binding") }
if sampler != nil {
d3d11.IUnknownRelease(unsafe.Pointer(sampler), sampler.Vtbl.Release)
}
if resView != nil {
d3d11.IUnknownRelease(unsafe.Pointer(resView), resView.Vtbl.Release)
}
d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
return nil, err
}
} }
resource := (*d3d11.Resource)(unsafe.Pointer(d3dtex.tex)) return &Texture{backend: b, format: d3dfmt, tex: tex, sampler: sampler, resView: resView, uaView: uaView, renderTarget: fbo, bindings: bindings, width: width, height: height}, nil
renderTarget, err := b.dev.CreateRenderTargetView(resource)
if err != nil {
return nil, err
}
fbo := &Framebuffer{ctx: b.ctx, dev: b.dev, format: d3dtex.format, resource: resource, renderTarget: renderTarget}
return fbo, nil
} }
func (b *Backend) newInputLayout(vertexShader shader.Sources, layout []driver.InputDesc) (*d3d11.InputLayout, error) { func (b *Backend) newInputLayout(vertexShader shader.Sources, layout []driver.InputDesc) (*d3d11.InputLayout, error) {
@@ -620,6 +615,12 @@ func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
} }
func (t *Texture) Release() { func (t *Texture) Release() {
if t.foreign {
panic("texture not created by NewTexture")
}
if t.renderTarget != nil {
d3d11.IUnknownRelease(unsafe.Pointer(t.renderTarget), t.renderTarget.Vtbl.Release)
}
if t.sampler != nil { if t.sampler != nil {
d3d11.IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.Vtbl.Release) d3d11.IUnknownRelease(unsafe.Pointer(t.sampler), t.sampler.Vtbl.Release)
} }
@@ -736,17 +737,14 @@ func (b *Buffer) Release() {
*b = Buffer{} *b = Buffer{}
} }
func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int) error { func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
if f.resource == nil {
return errors.New("framebuffer does not support ReadPixels")
}
w, h := src.Dx(), src.Dy() w, h := src.Dx(), src.Dy()
tex, err := f.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{ tex, err := t.backend.dev.CreateTexture2D(&d3d11.TEXTURE2D_DESC{
Width: uint32(w), Width: uint32(w),
Height: uint32(h), Height: uint32(h),
MipLevels: 1, MipLevels: 1,
ArraySize: 1, ArraySize: 1,
Format: f.format, Format: t.format,
SampleDesc: d3d11.DXGI_SAMPLE_DESC{ SampleDesc: d3d11.DXGI_SAMPLE_DESC{
Count: 1, Count: 1,
Quality: 0, Quality: 0,
@@ -759,11 +757,11 @@ func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int)
} }
defer d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release) defer d3d11.IUnknownRelease(unsafe.Pointer(tex), tex.Vtbl.Release)
res := (*d3d11.Resource)(unsafe.Pointer(tex)) res := (*d3d11.Resource)(unsafe.Pointer(tex))
f.ctx.CopySubresourceRegion( t.backend.ctx.CopySubresourceRegion(
res, res,
0, // Destination subresource. 0, // Destination subresource.
0, 0, 0, // Destination coordinates (x, y, z). 0, 0, 0, // Destination coordinates (x, y, z).
f.resource, (*d3d11.Resource)(t.tex),
0, // Source subresource. 0, // Source subresource.
&d3d11.BOX{ &d3d11.BOX{
Left: uint32(src.Min.X), Left: uint32(src.Min.X),
@@ -774,11 +772,11 @@ func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int)
Back: 1, Back: 1,
}, },
) )
resMap, err := f.ctx.Map(res, 0, d3d11.MAP_READ, 0) resMap, err := t.backend.ctx.Map(res, 0, d3d11.MAP_READ, 0)
if err != nil { if err != nil {
return fmt.Errorf("ReadPixels: %v", err) return fmt.Errorf("ReadPixels: %v", err)
} }
defer f.ctx.Unmap(res, 0) defer t.backend.ctx.Unmap(res, 0)
srcPitch := stride srcPitch := stride
dstPitch := int(resMap.RowPitch) dstPitch := int(resMap.RowPitch)
mapSize := dstPitch * h mapSize := dstPitch * h
@@ -797,30 +795,20 @@ func (b *Backend) BeginCompute() {
func (b *Backend) EndCompute() { func (b *Backend) EndCompute() {
} }
func (b *Backend) BeginRenderPass(fbo driver.Framebuffer, d driver.LoadDesc) { func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) {
b.fbo = fbo.(*Framebuffer) t := tex.(*Texture)
b.ctx.OMSetRenderTargets(b.fbo.renderTarget, nil) b.ctx.OMSetRenderTargets(t.renderTarget, nil)
if d.Action == driver.LoadActionClear { if d.Action == driver.LoadActionClear {
c := d.ClearColor c := d.ClearColor
b.clearColor = [4]float32{c.R, c.G, c.B, c.A} b.clearColor = [4]float32{c.R, c.G, c.B, c.A}
b.ctx.ClearRenderTargetView(b.fbo.renderTarget, &b.clearColor) b.ctx.ClearRenderTargetView(t.renderTarget, &b.clearColor)
} }
} }
func (b *Backend) EndRenderPass() { func (b *Backend) EndRenderPass() {
} }
func (f *Framebuffer) Release() { func (f *Texture) ImplementsRenderTarget() {}
if f.foreign {
panic("framebuffer not created by NewFramebuffer")
}
if f.renderTarget != nil {
d3d11.IUnknownRelease(unsafe.Pointer(f.renderTarget), f.renderTarget.Vtbl.Release)
f.renderTarget = nil
}
}
func (f *Framebuffer) ImplementsRenderTarget() {}
func convBufferBinding(typ driver.BufferBinding) uint32 { func convBufferBinding(typ driver.BufferBinding) uint32 {
var bindings uint32 var bindings uint32
+7 -12
View File
@@ -15,7 +15,7 @@ import (
// APIs such as OpenGL, Direct3D useful for rendering Gio // APIs such as OpenGL, Direct3D useful for rendering Gio
// operations. // operations.
type Device interface { type Device interface {
BeginFrame(target RenderTarget, clear bool, viewport image.Point) Framebuffer BeginFrame(target RenderTarget, clear bool, viewport image.Point) Texture
EndFrame() EndFrame()
Caps() Caps Caps() Caps
NewTimer() Timer NewTimer() Timer
@@ -23,7 +23,6 @@ type Device interface {
// are valid at the point of call. // are valid at the point of call.
IsTimeContinuous() bool IsTimeContinuous() bool
NewTexture(format TextureFormat, width, height int, minFilter, magFilter TextureFilter, bindings BufferBinding) (Texture, error) NewTexture(format TextureFormat, width, height int, minFilter, magFilter TextureFilter, bindings BufferBinding) (Texture, error)
NewFramebuffer(tex Texture) (Framebuffer, error)
NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error) NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error)
NewBuffer(typ BufferBinding, size int) (Buffer, error) NewBuffer(typ BufferBinding, size int) (Buffer, error)
NewComputeProgram(shader shader.Sources) (Program, error) NewComputeProgram(shader shader.Sources) (Program, error)
@@ -35,7 +34,7 @@ type Device interface {
DrawArrays(off, count int) DrawArrays(off, count int)
DrawElements(off, count int) DrawElements(off, count int)
BeginRenderPass(f Framebuffer, desc LoadDesc) BeginRenderPass(t Texture, desc LoadDesc)
EndRenderPass() EndRenderPass()
PrepareTexture(t Texture) PrepareTexture(t Texture)
BindProgram(p Program) BindProgram(p Program)
@@ -49,7 +48,7 @@ type Device interface {
BeginCompute() BeginCompute()
EndCompute() EndCompute()
CopyTexture(dst Texture, dstOrigin image.Point, src Framebuffer, srcRect image.Rectangle) CopyTexture(dst Texture, dstOrigin image.Point, src Texture, srcRect image.Rectangle)
MemoryBarrier() MemoryBarrier()
DispatchCompute(x, y, z int) DispatchCompute(x, y, z int)
@@ -133,12 +132,6 @@ type Buffer interface {
Download(data []byte) error Download(data []byte) error
} }
type Framebuffer interface {
RenderTarget
Release()
ReadPixels(src image.Rectangle, pixels []byte, stride int) error
}
type Timer interface { type Timer interface {
Begin() Begin()
End() End()
@@ -147,7 +140,9 @@ type Timer interface {
} }
type Texture interface { type Texture interface {
RenderTarget
Upload(offset, size image.Point, pixels []byte, stride int) Upload(offset, size image.Point, pixels []byte, stride int)
ReadPixels(src image.Rectangle, pixels []byte, stride int) error
Release() Release()
} }
@@ -210,9 +205,9 @@ func (f Features) Has(feats Features) bool {
return f&feats == feats return f&feats == feats
} }
func DownloadImage(d Device, f Framebuffer, r image.Rectangle) (*image.RGBA, error) { func DownloadImage(d Device, t Texture, r image.Rectangle) (*image.RGBA, error) {
img := image.NewRGBA(r) img := image.NewRGBA(r)
if err := f.ReadPixels(r, img.Pix, img.Stride); err != nil { if err := t.ReadPixels(r, img.Pix, img.Stride); err != nil {
return nil, err return nil, err
} }
if d.Caps().BottomLeftOrigin { if d.Caps().BottomLeftOrigin {
+18 -37
View File
@@ -407,6 +407,7 @@ type Texture struct {
sampler C.CFTypeRef sampler C.CFTypeRef
width int width int
height int height int
foreign bool
} }
type Shader struct { type Shader struct {
@@ -424,12 +425,6 @@ type Pipeline struct {
topology C.MTLPrimitiveType topology C.MTLPrimitiveType
} }
type Framebuffer struct {
backend *Backend
texture C.CFTypeRef
foreign bool
}
type Buffer struct { type Buffer struct {
backend *Backend backend *Backend
size int size int
@@ -469,7 +464,7 @@ func newMetalDevice(api driver.Metal) (driver.Device, error) {
return b, nil return b, nil
} }
func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Framebuffer { func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
if b.lastCmdBuffer != 0 { if b.lastCmdBuffer != 0 {
C.cmdBufferWaitUntilCompleted(b.lastCmdBuffer) C.cmdBufferWaitUntilCompleted(b.lastCmdBuffer)
b.stagingOff = 0 b.stagingOff = 0
@@ -477,13 +472,11 @@ func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport im
if target == nil { if target == nil {
return nil return nil
} }
var texture C.CFTypeRef
switch t := target.(type) { switch t := target.(type) {
case driver.MetalRenderTarget: case driver.MetalRenderTarget:
texture = C.CFTypeRef(t.Texture) texture := C.CFTypeRef(t.Texture)
return &Framebuffer{texture: texture, foreign: true} return &Texture{texture: texture, foreign: true}
case *Framebuffer: case *Texture:
texture = C.CFTypeRef(t.texture)
return t return t
default: default:
panic(fmt.Sprintf("metal: unsupported render target type: %T", t)) panic(fmt.Sprintf("metal: unsupported render target type: %T", t))
@@ -503,10 +496,10 @@ func (b *Backend) startBlit() C.CFTypeRef {
return b.blitEnc return b.blitEnc
} }
func (b *Backend) CopyTexture(dst driver.Texture, dorig image.Point, src driver.Framebuffer, srect image.Rectangle) { func (b *Backend) CopyTexture(dst driver.Texture, dorig image.Point, src driver.Texture, srect image.Rectangle) {
enc := b.startBlit() enc := b.startBlit()
dstTex := dst.(*Texture).texture dstTex := dst.(*Texture).texture
srcTex := src.(*Framebuffer).texture srcTex := src.(*Texture).texture
ssz := srect.Size() ssz := srect.Size()
C.blitEncCopyFromTexture( C.blitEncCopyFromTexture(
enc, enc,
@@ -718,13 +711,6 @@ func pixelFormatFor(f driver.TextureFormat) C.MTLPixelFormat {
} }
} }
func (b *Backend) NewFramebuffer(tex driver.Texture) (driver.Framebuffer, error) {
t := tex.(*Texture)
C.CFRetain(t.texture)
fbo := &Framebuffer{backend: b, texture: t.texture}
return fbo, nil
}
func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) { func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
// Transfer buffer contents in command encoders on every use for // Transfer buffer contents in command encoders on every use for
// smaller buffers. The advantage is that buffer re-use during a frame // smaller buffers. The advantage is that buffer re-use during a frame
@@ -921,6 +907,9 @@ func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
} }
func (t *Texture) Release() { func (t *Texture) Release() {
if t.foreign {
panic("metal: release of external texture")
}
C.CFRelease(t.texture) C.CFRelease(t.texture)
C.CFRelease(t.sampler) C.CFRelease(t.sampler)
*t = Texture{} *t = Texture{}
@@ -1076,7 +1065,7 @@ func (b *Buffer) Release() {
*b = Buffer{} *b = Buffer{}
} }
func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int) error { func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
if len(pixels) == 0 { if len(pixels) == 0 {
return nil return nil
} }
@@ -1092,10 +1081,10 @@ func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int)
} }
stageStride := sz.X * 4 stageStride := sz.X * 4
n := sz.Y * stageStride n := sz.Y * stageStride
buf, off := f.backend.stagingBuffer(n) buf, off := t.backend.stagingBuffer(n)
enc := f.backend.startBlit() enc := t.backend.startBlit()
C.blitEncCopyTextureToBuffer(enc, f.texture, buf, C.NSUInteger(off), C.NSUInteger(stageStride), C.NSUInteger(n), msize, orig) C.blitEncCopyTextureToBuffer(enc, t.texture, buf, C.NSUInteger(off), C.NSUInteger(stageStride), C.NSUInteger(n), msize, orig)
f.backend.endCmdBuffer(true) t.backend.endCmdBuffer(true)
store := bufferSlice(buf, off, n) store := bufferSlice(buf, off, n)
var srcOff, dstOff int var srcOff, dstOff int
for y := 0; y < sz.Y; y++ { for y := 0; y < sz.Y; y++ {
@@ -1108,10 +1097,10 @@ func (f *Framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int)
return nil return nil
} }
func (b *Backend) BeginRenderPass(fbo driver.Framebuffer, d driver.LoadDesc) { func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) {
b.endEncoder() b.endEncoder()
b.ensureCmdBuffer() b.ensureCmdBuffer()
f := fbo.(*Framebuffer) f := tex.(*Texture)
col := d.ClearColor col := d.ClearColor
var act C.MTLLoadAction var act C.MTLLoadAction
switch d.Action { switch d.Action {
@@ -1151,12 +1140,4 @@ func (b *Backend) endEncoder() {
} }
} }
func (f *Framebuffer) Release() { func (f *Texture) ImplementsRenderTarget() {}
if f.foreign {
panic("metal: invalid release of external framebuffer")
}
C.CFRelease(f.texture)
*f = Framebuffer{}
}
func (f *Framebuffer) ImplementsRenderTarget() {}
+42 -37
View File
@@ -98,14 +98,11 @@ type timer struct {
type texture struct { type texture struct {
backend *Backend backend *Backend
obj gl.Texture obj gl.Texture
fbo gl.Framebuffer
hasFBO bool
triple textureTriple triple textureTriple
width int width int
height int height int
}
type framebuffer struct {
backend *Backend
obj gl.Framebuffer
foreign bool foreign bool
} }
@@ -213,7 +210,7 @@ func newOpenGLDevice(api driver.OpenGL) (driver.Device, error) {
return b, nil return b, nil
} }
func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Framebuffer { func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
b.clear = clear b.clear = clear
b.glstate = b.queryState() b.glstate = b.queryState()
b.savedState = b.glstate b.savedState = b.glstate
@@ -223,8 +220,8 @@ func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport im
switch t := target.(type) { switch t := target.(type) {
case driver.OpenGLRenderTarget: case driver.OpenGLRenderTarget:
renderFBO = gl.Framebuffer(t) renderFBO = gl.Framebuffer(t)
case *framebuffer: case *texture:
renderFBO = t.obj renderFBO = t.ensureFBO()
default: default:
panic(fmt.Errorf("opengl: invalid render target type: %T", target)) panic(fmt.Errorf("opengl: invalid render target type: %T", target))
} }
@@ -265,7 +262,7 @@ func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport im
if b.sRGBFBO != nil && !clear { if b.sRGBFBO != nil && !clear {
b.clearOutput(0, 0, 0, 0) b.clearOutput(0, 0, 0, 0)
} }
return &framebuffer{backend: b, obj: renderFBO, foreign: true} return &texture{backend: b, fbo: renderFBO, hasFBO: true, foreign: true}
} }
func (b *Backend) EndFrame() { func (b *Backend) EndFrame() {
@@ -650,22 +647,30 @@ func (b *Backend) IsTimeContinuous() bool {
return b.funcs.GetInteger(gl.GPU_DISJOINT_EXT) == gl.FALSE return b.funcs.GetInteger(gl.GPU_DISJOINT_EXT) == gl.FALSE
} }
func (b *Backend) NewFramebuffer(tex driver.Texture) (driver.Framebuffer, error) { func (t *texture) ensureFBO() gl.Framebuffer {
if t.hasFBO {
return t.fbo
}
b := t.backend
oldFBO := b.glstate.drawFBO
defer func() {
b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, oldFBO)
}()
glErr(b.funcs) glErr(b.funcs)
gltex := tex.(*texture)
fb := b.funcs.CreateFramebuffer() fb := b.funcs.CreateFramebuffer()
fbo := &framebuffer{backend: b, obj: fb} b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fb)
b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo.obj)
if err := glErr(b.funcs); err != nil { if err := glErr(b.funcs); err != nil {
fbo.Release() b.funcs.DeleteFramebuffer(fb)
return nil, err panic(err)
} }
b.funcs.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, gltex.obj, 0) b.funcs.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, t.obj, 0)
if st := b.funcs.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE { if st := b.funcs.CheckFramebufferStatus(gl.FRAMEBUFFER); st != gl.FRAMEBUFFER_COMPLETE {
fbo.Release() b.funcs.DeleteFramebuffer(fb)
return nil, fmt.Errorf("incomplete framebuffer, status = 0x%x, err = %d", st, b.funcs.GetError()) panic(fmt.Errorf("incomplete framebuffer, status = 0x%x, err = %d", st, b.funcs.GetError()))
} }
return fbo, nil t.fbo = fb
t.hasFBO = true
return fb
} }
func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, binding driver.BufferBinding) (driver.Texture, error) { func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, binding driver.BufferBinding) (driver.Texture, error) {
@@ -1120,21 +1125,21 @@ func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
b.glstate.bindBuffer(b.funcs, gl.ELEMENT_ARRAY_BUFFER, gbuf.obj) b.glstate.bindBuffer(b.funcs, gl.ELEMENT_ARRAY_BUFFER, gbuf.obj)
} }
func (b *Backend) CopyTexture(dst driver.Texture, dstOrigin image.Point, src driver.Framebuffer, srcRect image.Rectangle) { func (b *Backend) CopyTexture(dst driver.Texture, dstOrigin image.Point, src driver.Texture, srcRect image.Rectangle) {
const unit = 0 const unit = 0
oldTex := b.glstate.texUnits.binds[unit] oldTex := b.glstate.texUnits.binds[unit]
defer func() { defer func() {
b.glstate.bindTexture(b.funcs, unit, oldTex) b.glstate.bindTexture(b.funcs, unit, oldTex)
}() }()
b.glstate.bindTexture(b.funcs, unit, dst.(*texture).obj) b.glstate.bindTexture(b.funcs, unit, dst.(*texture).obj)
b.glstate.bindFramebuffer(b.funcs, gl.READ_FRAMEBUFFER, src.(*framebuffer).obj) b.glstate.bindFramebuffer(b.funcs, gl.READ_FRAMEBUFFER, src.(*texture).ensureFBO())
sz := srcRect.Size() sz := srcRect.Size()
b.funcs.CopyTexSubImage2D(gl.TEXTURE_2D, 0, dstOrigin.X, dstOrigin.Y, srcRect.Min.X, srcRect.Min.Y, sz.X, sz.Y) b.funcs.CopyTexSubImage2D(gl.TEXTURE_2D, 0, dstOrigin.X, dstOrigin.Y, srcRect.Min.X, srcRect.Min.Y, sz.X, sz.Y)
} }
func (f *framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int) error { func (t *texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
glErr(f.backend.funcs) glErr(t.backend.funcs)
f.backend.glstate.bindFramebuffer(f.backend.funcs, gl.FRAMEBUFFER, f.obj) t.backend.glstate.bindFramebuffer(t.backend.funcs, gl.FRAMEBUFFER, t.ensureFBO())
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")
} }
@@ -1144,9 +1149,9 @@ func (f *framebuffer) ReadPixels(src image.Rectangle, pixels []byte, stride int)
if n := stride / 4; n != w { if n := stride / 4; n != w {
rowLen = n rowLen = n
} }
f.backend.glstate.pixelStorei(f.backend.funcs, gl.PACK_ROW_LENGTH, rowLen) t.backend.glstate.pixelStorei(t.backend.funcs, gl.PACK_ROW_LENGTH, rowLen)
f.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels) t.backend.funcs.ReadPixels(src.Min.X, src.Min.Y, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
return glErr(f.backend.funcs) return glErr(t.backend.funcs)
} }
func (b *Backend) BindPipeline(pl driver.Pipeline) { func (b *Backend) BindPipeline(pl driver.Pipeline) {
@@ -1163,8 +1168,9 @@ func (b *Backend) BeginCompute() {
func (b *Backend) EndCompute() { func (b *Backend) EndCompute() {
} }
func (b *Backend) BeginRenderPass(fbo driver.Framebuffer, desc driver.LoadDesc) { func (b *Backend) BeginRenderPass(tex driver.Texture, desc driver.LoadDesc) {
b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo.(*framebuffer).obj) fbo := tex.(*texture).ensureFBO()
b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo)
switch desc.Action { switch desc.Action {
case driver.LoadActionClear: case driver.LoadActionClear:
c := desc.ClearColor c := desc.ClearColor
@@ -1177,14 +1183,7 @@ func (b *Backend) BeginRenderPass(fbo driver.Framebuffer, desc driver.LoadDesc)
func (b *Backend) EndRenderPass() { func (b *Backend) EndRenderPass() {
} }
func (f *framebuffer) Release() { func (f *texture) ImplementsRenderTarget() {}
if f.foreign {
panic("framebuffer not created by NewFramebuffer")
}
f.backend.glstate.deleteFramebuffer(f.backend.funcs, f.obj)
}
func (f *framebuffer) ImplementsRenderTarget() {}
func (p *pipeline) Release() { func (p *pipeline) Release() {
p.prog.Release() p.prog.Release()
@@ -1209,6 +1208,12 @@ func (b *Backend) BindTexture(unit int, t driver.Texture) {
} }
func (t *texture) Release() { func (t *texture) Release() {
if t.foreign {
panic("texture not created by NewTexture")
}
if t.hasFBO {
t.backend.glstate.deleteFramebuffer(t.backend.funcs, t.fbo)
}
t.backend.glstate.deleteTexture(t.backend.funcs, t.obj) t.backend.glstate.deleteTexture(t.backend.funcs, t.obj)
} }
+1 -9
View File
@@ -95,7 +95,6 @@ type fboSet struct {
type stencilFBO struct { type stencilFBO struct {
size image.Point size image.Point
fbo driver.Framebuffer
tex driver.Texture tex driver.Texture
} }
@@ -257,8 +256,7 @@ func (s *fboSet) resize(ctx driver.Device, sizes []image.Point) {
waste := float32(sz.X*sz.Y) / float32(f.size.X*f.size.Y) waste := float32(sz.X*sz.Y) / float32(f.size.X*f.size.Y)
resize = resize || waste > 1.2 resize = resize || waste > 1.2
if resize { if resize {
if f.fbo != nil { if f.tex != nil {
f.fbo.Release()
f.tex.Release() f.tex.Release()
} }
tex, err := ctx.NewTexture(driver.TextureFormatFloat, sz.X, sz.Y, driver.FilterNearest, driver.FilterNearest, tex, err := ctx.NewTexture(driver.TextureFormatFloat, sz.X, sz.Y, driver.FilterNearest, driver.FilterNearest,
@@ -266,13 +264,8 @@ func (s *fboSet) resize(ctx driver.Device, sizes []image.Point) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
fbo, err := ctx.NewFramebuffer(tex)
if err != nil {
panic(err)
}
f.size = sz f.size = sz
f.tex = tex f.tex = tex
f.fbo = fbo
} }
} }
// Delete extra fbos. // Delete extra fbos.
@@ -282,7 +275,6 @@ func (s *fboSet) resize(ctx driver.Device, sizes []image.Point) {
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]
f.fbo.Release()
f.tex.Release() f.tex.Release()
} }
s.fbos = s.fbos[:idx] s.fbos = s.fbos[:idx]