graphicsdriver/metal: Asynchronous sending pixels

Fixes #1418
This commit is contained in:
Hajime Hoshi 2020-11-10 22:48:01 +09:00
parent 5705dc79fb
commit ddfb8adbc4

View File

@ -325,7 +325,7 @@ type Graphics struct {
transparent bool transparent bool
maxImageSize int maxImageSize int
tmpTexture mtl.Texture tmpTextures []mtl.Texture
pool unsafe.Pointer pool unsafe.Pointer
} }
@ -343,7 +343,7 @@ func (g *Graphics) Begin() {
} }
func (g *Graphics) End() { func (g *Graphics) End() {
g.flushIfNeeded(false, true) g.flushIfNeeded(true)
g.screenDrawable = ca.MetalDrawable{} g.screenDrawable = ca.MetalDrawable{}
C.releaseAutoreleasePool(g.pool) C.releaseAutoreleasePool(g.pool)
g.pool = nil g.pool = nil
@ -371,7 +371,7 @@ func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
g.ib = g.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&indices[0]), unsafe.Sizeof(indices[0])*uintptr(len(indices)), resourceStorageMode) g.ib = g.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&indices[0]), unsafe.Sizeof(indices[0])*uintptr(len(indices)), resourceStorageMode)
} }
func (g *Graphics) flushIfNeeded(wait bool, present bool) { func (g *Graphics) flushIfNeeded(present bool) {
if g.cb == (mtl.CommandBuffer{}) { if g.cb == (mtl.CommandBuffer{}) {
return return
} }
@ -380,9 +380,11 @@ func (g *Graphics) flushIfNeeded(wait bool, present bool) {
g.cb.PresentDrawable(g.screenDrawable) g.cb.PresentDrawable(g.screenDrawable)
} }
g.cb.Commit() g.cb.Commit()
if wait {
g.cb.WaitUntilCompleted() for _, t := range g.tmpTextures {
t.Release()
} }
g.tmpTextures = nil
g.cb = mtl.CommandBuffer{} g.cb = mtl.CommandBuffer{}
} }
@ -863,7 +865,7 @@ func (i *Image) Sync() <-chan struct{} {
return i.sync return i.sync
} }
i.graphics.flushIfNeeded(false, false) i.graphics.flushIfNeeded(false)
// Calling SynchronizeTexture is ignored on iOS (see mtl.m), but it looks like committing BlitCommandEncoder // Calling SynchronizeTexture is ignored on iOS (see mtl.m), but it looks like committing BlitCommandEncoder
// is necessary (#1337). // is necessary (#1337).
@ -903,26 +905,10 @@ func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
i.waitUntilSyncFinishes() i.waitUntilSyncFinishes()
g := i.graphics g := i.graphics
g.flushIfNeeded(true, false)
// If the memory is shared (e.g., iOS), texture data doen't have to be synced. Send the data directly. // Use a temporary texture to send pixels asynchrounsly, whichever the memory is shared (e.g., iOS) or
if storageMode == mtl.StorageModeShared { // managed (e.g., macOS). (#1418)
for _, a := range args {
i.texture.ReplaceRegion(mtl.Region{
Origin: mtl.Origin{X: a.X, Y: a.Y, Z: 0},
Size: mtl.Size{Width: a.Width, Height: a.Height, Depth: 1},
}, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Width)
}
}
// If the memory is managed (e.g., macOS), texture data cannot be sent to the destination directly because
// this requires synchronizing data between CPU and GPU. As synchronizing is inefficient, let's send the
// data to a temporary texture once, and then copy it in GPU.
w, h := i.texture.Width(), i.texture.Height() w, h := i.texture.Width(), i.texture.Height()
if g.tmpTexture == (mtl.Texture{}) || w > g.tmpTexture.Width() || h > g.tmpTexture.Height() {
if g.tmpTexture != (mtl.Texture{}) {
g.tmpTexture.Release()
}
td := mtl.TextureDescriptor{ td := mtl.TextureDescriptor{
TextureType: mtl.TextureType2D, TextureType: mtl.TextureType2D,
PixelFormat: mtl.PixelFormatRGBA8UNorm, PixelFormat: mtl.PixelFormatRGBA8UNorm,
@ -931,11 +917,11 @@ func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
StorageMode: storageMode, StorageMode: storageMode,
Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget, Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget,
} }
g.tmpTexture = g.view.getMTLDevice().MakeTexture(td) t := g.view.getMTLDevice().MakeTexture(td)
} g.tmpTextures = append(g.tmpTextures, t)
for _, a := range args { for _, a := range args {
g.tmpTexture.ReplaceRegion(mtl.Region{ t.ReplaceRegion(mtl.Region{
Origin: mtl.Origin{X: a.X, Y: a.Y, Z: 0}, Origin: mtl.Origin{X: a.X, Y: a.Y, Z: 0},
Size: mtl.Size{Width: a.Width, Height: a.Height, Depth: 1}, Size: mtl.Size{Width: a.Width, Height: a.Height, Depth: 1},
}, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Width) }, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Width)
@ -948,7 +934,7 @@ func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
for _, a := range args { for _, a := range args {
o := mtl.Origin{X: a.X, Y: a.Y, Z: 0} o := mtl.Origin{X: a.X, Y: a.Y, Z: 0}
s := mtl.Size{Width: a.Width, Height: a.Height, Depth: 1} s := mtl.Size{Width: a.Width, Height: a.Height, Depth: 1}
bce.CopyFromTexture(g.tmpTexture, 0, 0, o, s, i.texture, 0, 0, o) bce.CopyFromTexture(t, 0, 0, o, s, i.texture, 0, 0, o)
} }
bce.EndEncoding() bce.EndEncoding()
} }