mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
@ -316,6 +317,9 @@ type Graphics struct {
|
|||||||
|
|
||||||
screenDrawable ca.MetalDrawable
|
screenDrawable ca.MetalDrawable
|
||||||
|
|
||||||
|
buffers map[mtl.CommandBuffer][]mtl.Buffer
|
||||||
|
unusedBuffers map[mtl.Buffer]struct{}
|
||||||
|
|
||||||
lastDstTexture mtl.Texture
|
lastDstTexture mtl.Texture
|
||||||
lastStencilMode stencilMode
|
lastStencilMode stencilMode
|
||||||
|
|
||||||
@ -376,15 +380,90 @@ func (g *Graphics) SetUIView(uiview uintptr) {
|
|||||||
g.view.setUIView(uiview)
|
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) {
|
func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
|
||||||
if g.vb != (mtl.Buffer{}) {
|
vbSize := unsafe.Sizeof(vertices[0]) * uintptr(len(vertices))
|
||||||
g.vb.Release()
|
ibSize := unsafe.Sizeof(indices[0]) * uintptr(len(indices))
|
||||||
}
|
|
||||||
if g.ib != (mtl.Buffer{}) {
|
g.vb = g.availableBuffer(vbSize)
|
||||||
g.ib.Release()
|
g.vb.CopyToContents(unsafe.Pointer(&vertices[0]), vbSize)
|
||||||
}
|
|
||||||
g.vb = g.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&vertices[0]), unsafe.Sizeof(vertices[0])*uintptr(len(vertices)), resourceStorageMode)
|
g.ib = g.availableBuffer(ibSize)
|
||||||
g.ib = g.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&indices[0]), unsafe.Sizeof(indices[0])*uintptr(len(indices)), resourceStorageMode)
|
g.ib.CopyToContents(unsafe.Pointer(&indices[0]), ibSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Graphics) flushIfNeeded(present bool) {
|
func (g *Graphics) flushIfNeeded(present bool) {
|
||||||
|
@ -330,6 +330,17 @@ const (
|
|||||||
CompareFunctionAlways CompareFunction = 7
|
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
|
// Resource represents a memory allocation for storing specialized data
|
||||||
// that is accessible to the GPU.
|
// that is accessible to the GPU.
|
||||||
//
|
//
|
||||||
@ -623,6 +634,21 @@ type CommandBuffer struct {
|
|||||||
commandBuffer unsafe.Pointer
|
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.
|
// PresentDrawable registers a drawable presentation to occur as soon as possible.
|
||||||
//
|
//
|
||||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443029-presentdrawable.
|
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443029-presentdrawable.
|
||||||
@ -873,6 +899,12 @@ type Buffer struct {
|
|||||||
buffer unsafe.Pointer
|
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) {
|
func (b Buffer) CopyToContents(data unsafe.Pointer, lengthInBytes uintptr) {
|
||||||
C.Buffer_CopyToContents(b.buffer, data, C.size_t(lengthInBytes))
|
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_Release(void *commandQueue);
|
||||||
void *CommandQueue_MakeCommandBuffer(void *commandQueue);
|
void *CommandQueue_MakeCommandBuffer(void *commandQueue);
|
||||||
|
|
||||||
|
void CommandBuffer_Retain(void *commandBuffer);
|
||||||
void CommandBuffer_Release(void *commandBuffer);
|
void CommandBuffer_Release(void *commandBuffer);
|
||||||
|
uint8_t CommandBuffer_Status(void *commandBuffer);
|
||||||
void CommandBuffer_PresentDrawable(void *commandBuffer, void *drawable);
|
void CommandBuffer_PresentDrawable(void *commandBuffer, void *drawable);
|
||||||
void CommandBuffer_Commit(void *commandBuffer);
|
void CommandBuffer_Commit(void *commandBuffer);
|
||||||
void CommandBuffer_WaitUntilCompleted(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_Width(void *texture);
|
||||||
int Texture_Height(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_CopyToContents(void *buffer, void *data, size_t lengthInBytes);
|
||||||
void Buffer_Retain(void *buffer);
|
void Buffer_Retain(void *buffer);
|
||||||
void Buffer_Release(void *buffer);
|
void Buffer_Release(void *buffer);
|
||||||
|
@ -163,6 +163,18 @@ void *CommandQueue_MakeCommandBuffer(void *commandQueue) {
|
|||||||
return [(id<MTLCommandQueue>)commandQueue commandBuffer];
|
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) {
|
void CommandBuffer_PresentDrawable(void *commandBuffer, void *drawable) {
|
||||||
[(id<MTLCommandBuffer>)commandBuffer
|
[(id<MTLCommandBuffer>)commandBuffer
|
||||||
presentDrawable:(id<MTLDrawable>)drawable];
|
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]; }
|
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) {
|
void Buffer_CopyToContents(void *buffer, void *data, size_t lengthInBytes) {
|
||||||
memcpy(((id<MTLBuffer>)buffer).contents, data, 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]; }
|
void Buffer_Retain(void *buffer) { [(id<MTLBuffer>)buffer retain]; }
|
||||||
|
Loading…
Reference in New Issue
Block a user