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