mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 02:38:53 +01:00
internal/graphicsdriver/metal: Reuse MTLBuffer objects
In Metal, MTLBuffer objects are not 'transient' and are expensive to create. Reuse them whenever possible. See also: https://developer.apple.com/library/archive/documentation/Miscellaneous/Conceptual/MetalProgrammingGuide/Cmd-Submiss/Cmd-Submiss.html Updates #1196
This commit is contained in:
parent
be1a0e90e7
commit
ee2f891fcc
@ -20,6 +20,7 @@ package metal
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
@ -316,6 +317,9 @@ type Graphics struct {
|
||||
|
||||
screenDrawable ca.MetalDrawable
|
||||
|
||||
buffers map[mtl.CommandBuffer][]mtl.Buffer
|
||||
unusedBuffers map[mtl.Buffer]struct{}
|
||||
|
||||
lastDstTexture mtl.Texture
|
||||
lastStencilMode stencilMode
|
||||
|
||||
@ -376,15 +380,90 @@ func (g *Graphics) SetUIView(uiview uintptr) {
|
||||
g.view.setUIView(uiview)
|
||||
}
|
||||
|
||||
func pow2(x uintptr) uintptr {
|
||||
var p2 uintptr = 1
|
||||
for p2 < x {
|
||||
p2 *= 2
|
||||
}
|
||||
return p2
|
||||
}
|
||||
|
||||
func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer {
|
||||
if g.cb == (mtl.CommandBuffer{}) {
|
||||
g.cb = g.cq.MakeCommandBuffer()
|
||||
}
|
||||
|
||||
var newBuf mtl.Buffer
|
||||
|
||||
for cb, bs := range g.buffers {
|
||||
// If the command buffer still lives, the buffer must not be updated.
|
||||
// TODO: Handle an error?
|
||||
if cb.Status() != mtl.CommandBufferStatusCompleted {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, b := range bs {
|
||||
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
|
||||
delete(g.unusedBuffers, b)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// GC unused buffers.
|
||||
const maxUnusedBuffers = 20
|
||||
if len(g.unusedBuffers) > maxUnusedBuffers {
|
||||
bufs := make([]mtl.Buffer, 0, len(g.unusedBuffers))
|
||||
for b := range g.unusedBuffers {
|
||||
bufs = append(bufs, b)
|
||||
}
|
||||
sort.Slice(bufs, func(a, b int) bool {
|
||||
return bufs[a].Length() < bufs[b].Length()
|
||||
})
|
||||
for _, b := range bufs[maxUnusedBuffers:] {
|
||||
println("delete!", b.Length())
|
||||
delete(g.unusedBuffers, b)
|
||||
b.Release()
|
||||
}
|
||||
}
|
||||
|
||||
if newBuf == (mtl.Buffer{}) {
|
||||
newBuf = g.view.getMTLDevice().MakeBufferWithLength(pow2(length), resourceStorageMode)
|
||||
}
|
||||
|
||||
if g.buffers == nil {
|
||||
g.buffers = map[mtl.CommandBuffer][]mtl.Buffer{}
|
||||
}
|
||||
if _, ok := g.buffers[g.cb]; !ok {
|
||||
g.cb.Retain()
|
||||
}
|
||||
g.buffers[g.cb] = append(g.buffers[g.cb], newBuf)
|
||||
return newBuf
|
||||
}
|
||||
|
||||
func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
|
||||
if g.vb != (mtl.Buffer{}) {
|
||||
g.vb.Release()
|
||||
}
|
||||
if g.ib != (mtl.Buffer{}) {
|
||||
g.ib.Release()
|
||||
}
|
||||
g.vb = g.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&vertices[0]), unsafe.Sizeof(vertices[0])*uintptr(len(vertices)), resourceStorageMode)
|
||||
g.ib = g.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&indices[0]), unsafe.Sizeof(indices[0])*uintptr(len(indices)), resourceStorageMode)
|
||||
vbSize := unsafe.Sizeof(vertices[0]) * uintptr(len(vertices))
|
||||
ibSize := unsafe.Sizeof(indices[0]) * uintptr(len(indices))
|
||||
|
||||
g.vb = g.availableBuffer(vbSize)
|
||||
g.vb.CopyToContents(unsafe.Pointer(&vertices[0]), vbSize)
|
||||
|
||||
g.ib = g.availableBuffer(ibSize)
|
||||
g.ib.CopyToContents(unsafe.Pointer(&indices[0]), ibSize)
|
||||
}
|
||||
|
||||
func (g *Graphics) flushIfNeeded(present bool) {
|
||||
|
@ -330,6 +330,17 @@ const (
|
||||
CompareFunctionAlways CompareFunction = 7
|
||||
)
|
||||
|
||||
type CommandBufferStatus uint8
|
||||
|
||||
const (
|
||||
CommandBufferStatusNotEnqueued CommandBufferStatus = 0 //The command buffer is not enqueued yet.
|
||||
CommandBufferStatusEnqueued CommandBufferStatus = 1 // The command buffer is enqueued.
|
||||
CommandBufferStatusCommitted CommandBufferStatus = 2 // The command buffer is committed for execution.
|
||||
CommandBufferStatusScheduled CommandBufferStatus = 3 // The command buffer is scheduled.
|
||||
CommandBufferStatusCompleted CommandBufferStatus = 4 // The command buffer completed execution successfully.
|
||||
CommandBufferStatusError CommandBufferStatus = 5 // Execution of the command buffer was aborted due to an error during execution.
|
||||
)
|
||||
|
||||
// Resource represents a memory allocation for storing specialized data
|
||||
// that is accessible to the GPU.
|
||||
//
|
||||
@ -623,6 +634,21 @@ type CommandBuffer struct {
|
||||
commandBuffer unsafe.Pointer
|
||||
}
|
||||
|
||||
func (cb CommandBuffer) Retain() {
|
||||
C.CommandBuffer_Retain(cb.commandBuffer)
|
||||
}
|
||||
|
||||
func (cb CommandBuffer) Release() {
|
||||
C.CommandBuffer_Release(cb.commandBuffer)
|
||||
}
|
||||
|
||||
// Status returns the current stage in the lifetime of the command buffer.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443048-status
|
||||
func (cb CommandBuffer) Status() CommandBufferStatus {
|
||||
return CommandBufferStatus(C.CommandBuffer_Status(cb.commandBuffer))
|
||||
}
|
||||
|
||||
// PresentDrawable registers a drawable presentation to occur as soon as possible.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443029-presentdrawable.
|
||||
@ -873,6 +899,12 @@ type Buffer struct {
|
||||
buffer unsafe.Pointer
|
||||
}
|
||||
|
||||
func (b Buffer) resource() unsafe.Pointer { return b.buffer }
|
||||
|
||||
func (b Buffer) Length() uintptr {
|
||||
return uintptr(C.Buffer_Length(b.buffer))
|
||||
}
|
||||
|
||||
func (b Buffer) CopyToContents(data unsafe.Pointer, lengthInBytes uintptr) {
|
||||
C.Buffer_CopyToContents(b.buffer, data, C.size_t(lengthInBytes))
|
||||
}
|
||||
|
@ -147,7 +147,9 @@ void *Device_MakeDepthStencilState(void *device,
|
||||
void CommandQueue_Release(void *commandQueue);
|
||||
void *CommandQueue_MakeCommandBuffer(void *commandQueue);
|
||||
|
||||
void CommandBuffer_Retain(void *commandBuffer);
|
||||
void CommandBuffer_Release(void *commandBuffer);
|
||||
uint8_t CommandBuffer_Status(void *commandBuffer);
|
||||
void CommandBuffer_PresentDrawable(void *commandBuffer, void *drawable);
|
||||
void CommandBuffer_Commit(void *commandBuffer);
|
||||
void CommandBuffer_WaitUntilCompleted(void *commandBuffer);
|
||||
@ -208,6 +210,7 @@ void Texture_ReplaceRegion(void *texture, struct Region region, uint_t level,
|
||||
int Texture_Width(void *texture);
|
||||
int Texture_Height(void *texture);
|
||||
|
||||
size_t Buffer_Length(void *buffer);
|
||||
void Buffer_CopyToContents(void *buffer, void *data, size_t lengthInBytes);
|
||||
void Buffer_Retain(void *buffer);
|
||||
void Buffer_Release(void *buffer);
|
||||
|
@ -163,6 +163,18 @@ void *CommandQueue_MakeCommandBuffer(void *commandQueue) {
|
||||
return [(id<MTLCommandQueue>)commandQueue commandBuffer];
|
||||
}
|
||||
|
||||
void CommandBuffer_Retain(void *commandBuffer) {
|
||||
[(id<MTLCommandBuffer>)commandBuffer retain];
|
||||
}
|
||||
|
||||
void CommandBuffer_Release(void *commandBuffer) {
|
||||
[(id<MTLCommandBuffer>)commandBuffer release];
|
||||
}
|
||||
|
||||
uint8_t CommandBuffer_Status(void *commandBuffer) {
|
||||
return [(id<MTLCommandBuffer>)commandBuffer status];
|
||||
}
|
||||
|
||||
void CommandBuffer_PresentDrawable(void *commandBuffer, void *drawable) {
|
||||
[(id<MTLCommandBuffer>)commandBuffer
|
||||
presentDrawable:(id<MTLDrawable>)drawable];
|
||||
@ -401,8 +413,13 @@ int Texture_Width(void *texture) { return [(id<MTLTexture>)texture width]; }
|
||||
|
||||
int Texture_Height(void *texture) { return [(id<MTLTexture>)texture height]; }
|
||||
|
||||
size_t Buffer_Length(void *buffer) { return [(id<MTLBuffer>)buffer length]; }
|
||||
|
||||
void Buffer_CopyToContents(void *buffer, void *data, size_t lengthInBytes) {
|
||||
memcpy(((id<MTLBuffer>)buffer).contents, data, lengthInBytes);
|
||||
#if !TARGET_OS_IPHONE
|
||||
[(id<MTLBuffer>)buffer didModifyRange:NSMakeRange(0, lengthInBytes)];
|
||||
#endif
|
||||
}
|
||||
|
||||
void Buffer_Retain(void *buffer) { [(id<MTLBuffer>)buffer retain]; }
|
||||
|
Loading…
Reference in New Issue
Block a user