mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 19:28:57 +01:00
internal/graphicsdriver/metal: Do not retain MTLCommandBuffer for MTLBuffer
Before this change, a command buffer is retained indirectly by a buffer, and this might extend the life of drawable unexpectedly. This change stops using command buffers as a key of the buffers pool, and use a counter increated by nextDrawable calls. Updates #1196
This commit is contained in:
parent
80ac0646d5
commit
e0fbfc2bb0
@ -317,8 +317,7 @@ type Graphics struct {
|
|||||||
|
|
||||||
screenDrawable ca.MetalDrawable
|
screenDrawable ca.MetalDrawable
|
||||||
|
|
||||||
buffers map[mtl.CommandBuffer][]mtl.Buffer
|
buffers map[mtl.Buffer]int
|
||||||
unusedBuffers map[mtl.Buffer]struct{}
|
|
||||||
|
|
||||||
lastDstTexture mtl.Texture
|
lastDstTexture mtl.Texture
|
||||||
lastStencilMode stencilMode
|
lastStencilMode stencilMode
|
||||||
@ -388,54 +387,40 @@ func pow2(x uintptr) uintptr {
|
|||||||
return p2
|
return p2
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer {
|
func (g *Graphics) ageBuffers() {
|
||||||
if g.cb == (mtl.CommandBuffer{}) {
|
for b, age := range g.buffers {
|
||||||
g.cb = g.cq.MakeCommandBuffer()
|
if age <= maximumDrawableCount {
|
||||||
|
g.buffers[b]++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer {
|
||||||
var newBuf mtl.Buffer
|
var newBuf mtl.Buffer
|
||||||
|
|
||||||
for cb, bs := range g.buffers {
|
var availBufs []mtl.Buffer
|
||||||
// If the command buffer still lives, the buffer must not be updated.
|
for b, age := range g.buffers {
|
||||||
// TODO: Handle an error?
|
// If the buffer is too young, its command buffer might still live.
|
||||||
if cb.Status() != mtl.CommandBufferStatusCompleted {
|
// The buffer must not be updated in this case.
|
||||||
|
if age <= maximumDrawableCount {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, b := range bs {
|
if newBuf == (mtl.Buffer{}) && b.Length() >= length {
|
||||||
if newBuf == (mtl.Buffer{}) && b.Length() >= length {
|
|
||||||
newBuf = b
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if g.unusedBuffers == nil {
|
|
||||||
g.unusedBuffers = map[mtl.Buffer]struct{}{}
|
|
||||||
}
|
|
||||||
g.unusedBuffers[b] = struct{}{}
|
|
||||||
}
|
|
||||||
delete(g.buffers, cb)
|
|
||||||
cb.Release()
|
|
||||||
}
|
|
||||||
|
|
||||||
for b := range g.unusedBuffers {
|
|
||||||
if b.Length() >= length {
|
|
||||||
newBuf = b
|
newBuf = b
|
||||||
delete(g.unusedBuffers, b)
|
continue
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
availBufs = append(availBufs, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GC unused buffers.
|
// GC unused buffers.
|
||||||
const maxUnusedBuffers = 10
|
const maxUnusedBuffers = 10
|
||||||
if len(g.unusedBuffers) > maxUnusedBuffers {
|
if len(availBufs) > maxUnusedBuffers {
|
||||||
bufs := make([]mtl.Buffer, 0, len(g.unusedBuffers))
|
sort.Slice(availBufs, func(a, b int) bool {
|
||||||
for b := range g.unusedBuffers {
|
return availBufs[a].Length() > availBufs[b].Length()
|
||||||
bufs = append(bufs, b)
|
|
||||||
}
|
|
||||||
sort.Slice(bufs, func(a, b int) bool {
|
|
||||||
return bufs[a].Length() > bufs[b].Length()
|
|
||||||
})
|
})
|
||||||
for _, b := range bufs[maxUnusedBuffers:] {
|
for _, b := range availBufs[maxUnusedBuffers:] {
|
||||||
delete(g.unusedBuffers, b)
|
delete(g.buffers, b)
|
||||||
b.Release()
|
b.Release()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -443,14 +428,10 @@ func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer {
|
|||||||
if newBuf == (mtl.Buffer{}) {
|
if newBuf == (mtl.Buffer{}) {
|
||||||
newBuf = g.view.getMTLDevice().MakeBufferWithLength(pow2(length), resourceStorageMode)
|
newBuf = g.view.getMTLDevice().MakeBufferWithLength(pow2(length), resourceStorageMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.buffers == nil {
|
if g.buffers == nil {
|
||||||
g.buffers = map[mtl.CommandBuffer][]mtl.Buffer{}
|
g.buffers = map[mtl.Buffer]int{}
|
||||||
}
|
}
|
||||||
if _, ok := g.buffers[g.cb]; !ok {
|
g.buffers[newBuf] = 0
|
||||||
g.cb.Retain()
|
|
||||||
}
|
|
||||||
g.buffers[g.cb] = append(g.buffers[g.cb], newBuf)
|
|
||||||
return newBuf
|
return newBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1229,6 +1210,10 @@ func (i *Image) mtlTexture() mtl.Texture {
|
|||||||
return mtl.Texture{}
|
return mtl.Texture{}
|
||||||
}
|
}
|
||||||
g.screenDrawable = drawable
|
g.screenDrawable = drawable
|
||||||
|
|
||||||
|
// nextDrawable blocks until the new drawable is available.
|
||||||
|
// If nextDrawable returns a valid drawable, this means that at least one drawable is already processed.
|
||||||
|
g.ageBuffers()
|
||||||
}
|
}
|
||||||
return g.screenDrawable.Texture()
|
return g.screenDrawable.Texture()
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
|
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const maximumDrawableCount = 3
|
||||||
|
|
||||||
type view struct {
|
type view struct {
|
||||||
window uintptr
|
window uintptr
|
||||||
uiview uintptr
|
uiview uintptr
|
||||||
@ -69,7 +71,7 @@ func (v *view) reset() error {
|
|||||||
// MTLPixelFormatBGRA8Unorm_sRGB, MTLPixelFormatRGBA16Float, MTLPixelFormatBGRA10_XR, or
|
// MTLPixelFormatBGRA8Unorm_sRGB, MTLPixelFormatRGBA16Float, MTLPixelFormatBGRA10_XR, or
|
||||||
// MTLPixelFormatBGRA10_XR_sRGB.
|
// MTLPixelFormatBGRA10_XR_sRGB.
|
||||||
v.ml.SetPixelFormat(mtl.PixelFormatBGRA8UNorm)
|
v.ml.SetPixelFormat(mtl.PixelFormatBGRA8UNorm)
|
||||||
v.ml.SetMaximumDrawableCount(3)
|
v.ml.SetMaximumDrawableCount(maximumDrawableCount)
|
||||||
|
|
||||||
// The vsync state might be reset. Set the state again (#1364).
|
// The vsync state might be reset. Set the state again (#1364).
|
||||||
v.ml.SetDisplaySyncEnabled(v.vsync)
|
v.ml.SetDisplaySyncEnabled(v.vsync)
|
||||||
|
Loading…
Reference in New Issue
Block a user