graphicsdriver/metal: Rename Driver -> Graphics

This commit is contained in:
Hajime Hoshi 2020-04-04 17:12:24 +09:00
parent 6cbf37e855
commit 01d1afa25c

View File

@ -283,7 +283,7 @@ type rpsKey struct {
screen bool screen bool
} }
type Driver struct { type Graphics struct {
view view view view
screenRPS mtl.RenderPipelineState screenRPS mtl.RenderPipelineState
@ -307,91 +307,91 @@ type Driver struct {
pool unsafe.Pointer pool unsafe.Pointer
} }
var theDriver Driver var theGraphics Graphics
func Get() *Driver { func Get() *Graphics {
return &theDriver return &theGraphics
} }
func (d *Driver) SetThread(thread *thread.Thread) { func (g *Graphics) SetThread(thread *thread.Thread) {
d.t = thread g.t = thread
} }
func (d *Driver) Begin() { func (g *Graphics) Begin() {
d.t.Call(func() error { g.t.Call(func() error {
// NSAutoreleasePool is required to release drawable correctly (#847). // NSAutoreleasePool is required to release drawable correctly (#847).
// https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/Drawables.html // https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/Drawables.html
d.pool = C.allocAutoreleasePool() g.pool = C.allocAutoreleasePool()
return nil return nil
}) })
} }
func (d *Driver) End() { func (g *Graphics) End() {
d.flush(false, true) g.flush(false, true)
d.t.Call(func() error { g.t.Call(func() error {
d.screenDrawable = ca.MetalDrawable{} g.screenDrawable = ca.MetalDrawable{}
C.releaseAutoreleasePool(d.pool) C.releaseAutoreleasePool(g.pool)
d.pool = nil g.pool = nil
return nil return nil
}) })
} }
func (d *Driver) SetWindow(window unsafe.Pointer) { func (g *Graphics) SetWindow(window unsafe.Pointer) {
d.t.Call(func() error { g.t.Call(func() error {
// Note that [NSApp mainWindow] returns nil when the window is borderless. // Note that [NSApp mainWindow] returns nil when the window is borderless.
// Then the window is needed to be given explicitly. // Then the window is needed to be given explicitly.
d.view.setWindow(window) g.view.setWindow(window)
return nil return nil
}) })
} }
func (d *Driver) SetUIView(uiview uintptr) { func (g *Graphics) SetUIView(uiview uintptr) {
// TODO: Should this be called on the main thread? // TODO: Should this be called on the main thread?
d.view.setUIView(uiview) g.view.setUIView(uiview)
} }
func (d *Driver) SetVertices(vertices []float32, indices []uint16) { func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
d.t.Call(func() error { g.t.Call(func() error {
if d.vb != (mtl.Buffer{}) { if g.vb != (mtl.Buffer{}) {
d.vb.Release() g.vb.Release()
} }
if d.ib != (mtl.Buffer{}) { if g.ib != (mtl.Buffer{}) {
d.ib.Release() g.ib.Release()
} }
d.vb = d.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&vertices[0]), unsafe.Sizeof(vertices[0])*uintptr(len(vertices)), resourceStorageMode) g.vb = g.view.getMTLDevice().MakeBufferWithBytes(unsafe.Pointer(&vertices[0]), unsafe.Sizeof(vertices[0])*uintptr(len(vertices)), resourceStorageMode)
d.ib = d.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)
return nil return nil
}) })
} }
func (d *Driver) flush(wait bool, present bool) { func (g *Graphics) flush(wait bool, present bool) {
d.t.Call(func() error { g.t.Call(func() error {
if d.cb == (mtl.CommandBuffer{}) { if g.cb == (mtl.CommandBuffer{}) {
return nil return nil
} }
if present && d.screenDrawable != (ca.MetalDrawable{}) { if present && g.screenDrawable != (ca.MetalDrawable{}) {
d.cb.PresentDrawable(d.screenDrawable) g.cb.PresentDrawable(g.screenDrawable)
} }
d.cb.Commit() g.cb.Commit()
if wait { if wait {
d.cb.WaitUntilCompleted() g.cb.WaitUntilCompleted()
} }
d.cb = mtl.CommandBuffer{} g.cb = mtl.CommandBuffer{}
return nil return nil
}) })
} }
func (d *Driver) checkSize(width, height int) { func (g *Graphics) checkSize(width, height int) {
if width < 1 { if width < 1 {
panic(fmt.Sprintf("metal: width (%d) must be equal or more than %d", width, 1)) panic(fmt.Sprintf("metal: width (%d) must be equal or more than %d", width, 1))
} }
if height < 1 { if height < 1 {
panic(fmt.Sprintf("metal: height (%d) must be equal or more than %d", height, 1)) panic(fmt.Sprintf("metal: height (%d) must be equal or more than %d", height, 1))
} }
m := d.MaxImageSize() m := g.MaxImageSize()
if width > m { if width > m {
panic(fmt.Sprintf("metal: width (%d) must be less than or equal to %d", width, m)) panic(fmt.Sprintf("metal: width (%d) must be less than or equal to %d", width, m))
} }
@ -400,8 +400,8 @@ func (d *Driver) checkSize(width, height int) {
} }
} }
func (d *Driver) NewImage(width, height int) (driver.Image, error) { func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
d.checkSize(width, height) g.checkSize(width, height)
td := mtl.TextureDescriptor{ td := mtl.TextureDescriptor{
PixelFormat: mtl.PixelFormatRGBA8UNorm, PixelFormat: mtl.PixelFormatRGBA8UNorm,
Width: graphics.InternalImageSize(width), Width: graphics.InternalImageSize(width),
@ -410,52 +410,52 @@ func (d *Driver) NewImage(width, height int) (driver.Image, error) {
Usage: textureUsage, Usage: textureUsage,
} }
var t mtl.Texture var t mtl.Texture
d.t.Call(func() error { g.t.Call(func() error {
t = d.view.getMTLDevice().MakeTexture(td) t = g.view.getMTLDevice().MakeTexture(td)
return nil return nil
}) })
return &Image{ return &Image{
driver: d, graphics: g,
width: width, width: width,
height: height, height: height,
texture: t, texture: t,
}, nil }, nil
} }
func (d *Driver) NewScreenFramebufferImage(width, height int) (driver.Image, error) { func (g *Graphics) NewScreenFramebufferImage(width, height int) (driver.Image, error) {
d.t.Call(func() error { g.t.Call(func() error {
d.view.setDrawableSize(width, height) g.view.setDrawableSize(width, height)
return nil return nil
}) })
return &Image{ return &Image{
driver: d, graphics: g,
width: width, width: width,
height: height, height: height,
screen: true, screen: true,
}, nil }, nil
} }
func (d *Driver) SetTransparent(transparent bool) { func (g *Graphics) SetTransparent(transparent bool) {
d.transparent = transparent g.transparent = transparent
} }
func (d *Driver) Reset() error { func (g *Graphics) Reset() error {
if err := d.t.Call(func() error { if err := g.t.Call(func() error {
if d.cq != (mtl.CommandQueue{}) { if g.cq != (mtl.CommandQueue{}) {
d.cq.Release() g.cq.Release()
d.cq = mtl.CommandQueue{} g.cq = mtl.CommandQueue{}
} }
// TODO: Release existing rpss // TODO: Release existing rpss
if d.rpss == nil { if g.rpss == nil {
d.rpss = map[rpsKey]mtl.RenderPipelineState{} g.rpss = map[rpsKey]mtl.RenderPipelineState{}
} }
if err := d.view.reset(); err != nil { if err := g.view.reset(); err != nil {
return err return err
} }
if d.transparent { if g.transparent {
d.view.ml.SetOpaque(false) g.view.ml.SetOpaque(false)
} }
replaces := map[string]string{ replaces := map[string]string{
@ -470,7 +470,7 @@ func (d *Driver) Reset() error {
src = strings.Replace(src, k, v, -1) src = strings.Replace(src, k, v, -1)
} }
lib, err := d.view.getMTLDevice().MakeLibrary(src, mtl.CompileOptions{}) lib, err := g.view.getMTLDevice().MakeLibrary(src, mtl.CompileOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -487,17 +487,17 @@ func (d *Driver) Reset() error {
VertexFunction: vs, VertexFunction: vs,
FragmentFunction: fs, FragmentFunction: fs,
} }
rpld.ColorAttachments[0].PixelFormat = d.view.colorPixelFormat() rpld.ColorAttachments[0].PixelFormat = g.view.colorPixelFormat()
rpld.ColorAttachments[0].BlendingEnabled = true rpld.ColorAttachments[0].BlendingEnabled = true
rpld.ColorAttachments[0].DestinationAlphaBlendFactor = mtl.BlendFactorZero rpld.ColorAttachments[0].DestinationAlphaBlendFactor = mtl.BlendFactorZero
rpld.ColorAttachments[0].DestinationRGBBlendFactor = mtl.BlendFactorZero rpld.ColorAttachments[0].DestinationRGBBlendFactor = mtl.BlendFactorZero
rpld.ColorAttachments[0].SourceAlphaBlendFactor = mtl.BlendFactorOne rpld.ColorAttachments[0].SourceAlphaBlendFactor = mtl.BlendFactorOne
rpld.ColorAttachments[0].SourceRGBBlendFactor = mtl.BlendFactorOne rpld.ColorAttachments[0].SourceRGBBlendFactor = mtl.BlendFactorOne
rps, err := d.view.getMTLDevice().MakeRenderPipelineState(rpld) rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld)
if err != nil { if err != nil {
return err return err
} }
d.screenRPS = rps g.screenRPS = rps
conv := func(c driver.Operation) mtl.BlendFactor { conv := func(c driver.Operation) mtl.BlendFactor {
switch c { switch c {
@ -544,7 +544,7 @@ func (d *Driver) Reset() error {
pix := mtl.PixelFormatRGBA8UNorm pix := mtl.PixelFormatRGBA8UNorm
if screen { if screen {
pix = d.view.colorPixelFormat() pix = g.view.colorPixelFormat()
} }
rpld.ColorAttachments[0].PixelFormat = pix rpld.ColorAttachments[0].PixelFormat = pix
rpld.ColorAttachments[0].BlendingEnabled = true rpld.ColorAttachments[0].BlendingEnabled = true
@ -554,11 +554,11 @@ func (d *Driver) Reset() error {
rpld.ColorAttachments[0].DestinationRGBBlendFactor = conv(dst) rpld.ColorAttachments[0].DestinationRGBBlendFactor = conv(dst)
rpld.ColorAttachments[0].SourceAlphaBlendFactor = conv(src) rpld.ColorAttachments[0].SourceAlphaBlendFactor = conv(src)
rpld.ColorAttachments[0].SourceRGBBlendFactor = conv(src) rpld.ColorAttachments[0].SourceRGBBlendFactor = conv(src)
rps, err := d.view.getMTLDevice().MakeRenderPipelineState(rpld) rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld)
if err != nil { if err != nil {
return err return err
} }
d.rpss[rpsKey{ g.rpss[rpsKey{
screen: screen, screen: screen,
useColorM: cm, useColorM: cm,
filter: f, filter: f,
@ -571,7 +571,7 @@ func (d *Driver) Reset() error {
} }
} }
d.cq = d.view.getMTLDevice().MakeCommandQueue() g.cq = g.view.getMTLDevice().MakeCommandQueue()
return nil return nil
}); err != nil { }); err != nil {
return err return err
@ -580,11 +580,11 @@ func (d *Driver) Reset() error {
return nil return nil
} }
func (d *Driver) Draw(indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address) error { func (g *Graphics) Draw(indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address) error {
d.drawCalled = true g.drawCalled = true
if err := d.t.Call(func() error { if err := g.t.Call(func() error {
d.view.update() g.view.update()
rpd := mtl.RenderPassDescriptor{} rpd := mtl.RenderPassDescriptor{}
// Even though the destination pixels are not used, mtl.LoadActionDontCare might cause glitches // Even though the destination pixels are not used, mtl.LoadActionDontCare might cause glitches
@ -593,33 +593,33 @@ func (d *Driver) Draw(indexLen int, indexOffset int, mode driver.CompositeMode,
rpd.ColorAttachments[0].StoreAction = mtl.StoreActionStore rpd.ColorAttachments[0].StoreAction = mtl.StoreActionStore
var t mtl.Texture var t mtl.Texture
if d.dst.screen { if g.dst.screen {
if d.screenDrawable == (ca.MetalDrawable{}) { if g.screenDrawable == (ca.MetalDrawable{}) {
drawable := d.view.drawable() drawable := g.view.drawable()
if drawable == (ca.MetalDrawable{}) { if drawable == (ca.MetalDrawable{}) {
return nil return nil
} }
d.screenDrawable = drawable g.screenDrawable = drawable
} }
t = d.screenDrawable.Texture() t = g.screenDrawable.Texture()
} else { } else {
t = d.dst.texture t = g.dst.texture
} }
rpd.ColorAttachments[0].Texture = t rpd.ColorAttachments[0].Texture = t
rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{} rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{}
w, h := d.dst.viewportSize() w, h := g.dst.viewportSize()
if d.cb == (mtl.CommandBuffer{}) { if g.cb == (mtl.CommandBuffer{}) {
d.cb = d.cq.MakeCommandBuffer() g.cb = g.cq.MakeCommandBuffer()
} }
rce := d.cb.MakeRenderCommandEncoder(rpd) rce := g.cb.MakeRenderCommandEncoder(rpd)
if d.dst.screen && filter == driver.FilterScreen { if g.dst.screen && filter == driver.FilterScreen {
rce.SetRenderPipelineState(d.screenRPS) rce.SetRenderPipelineState(g.screenRPS)
} else { } else {
rce.SetRenderPipelineState(d.rpss[rpsKey{ rce.SetRenderPipelineState(g.rpss[rpsKey{
screen: d.dst.screen, screen: g.dst.screen,
useColorM: colorM != nil, useColorM: colorM != nil,
filter: filter, filter: filter,
address: address, address: address,
@ -634,14 +634,14 @@ func (d *Driver) Draw(indexLen int, indexOffset int, mode driver.CompositeMode,
ZNear: -1, ZNear: -1,
ZFar: 1, ZFar: 1,
}) })
rce.SetVertexBuffer(d.vb, 0, 0) rce.SetVertexBuffer(g.vb, 0, 0)
viewportSize := [...]float32{float32(w), float32(h)} viewportSize := [...]float32{float32(w), float32(h)}
rce.SetVertexBytes(unsafe.Pointer(&viewportSize[0]), unsafe.Sizeof(viewportSize), 1) rce.SetVertexBytes(unsafe.Pointer(&viewportSize[0]), unsafe.Sizeof(viewportSize), 1)
sourceSize := [...]float32{ sourceSize := [...]float32{
float32(graphics.InternalImageSize(d.src.width)), float32(graphics.InternalImageSize(g.src.width)),
float32(graphics.InternalImageSize(d.src.height)), float32(graphics.InternalImageSize(g.src.height)),
} }
rce.SetFragmentBytes(unsafe.Pointer(&sourceSize[0]), unsafe.Sizeof(sourceSize), 2) rce.SetFragmentBytes(unsafe.Pointer(&sourceSize[0]), unsafe.Sizeof(sourceSize), 2)
@ -649,15 +649,15 @@ func (d *Driver) Draw(indexLen int, indexOffset int, mode driver.CompositeMode,
rce.SetFragmentBytes(unsafe.Pointer(&esBody[0]), unsafe.Sizeof(esBody[0])*uintptr(len(esBody)), 3) rce.SetFragmentBytes(unsafe.Pointer(&esBody[0]), unsafe.Sizeof(esBody[0])*uintptr(len(esBody)), 3)
rce.SetFragmentBytes(unsafe.Pointer(&esTranslate[0]), unsafe.Sizeof(esTranslate[0])*uintptr(len(esTranslate)), 4) rce.SetFragmentBytes(unsafe.Pointer(&esTranslate[0]), unsafe.Sizeof(esTranslate[0])*uintptr(len(esTranslate)), 4)
scale := float32(d.dst.width) / float32(d.src.width) scale := float32(g.dst.width) / float32(g.src.width)
rce.SetFragmentBytes(unsafe.Pointer(&scale), unsafe.Sizeof(scale), 5) rce.SetFragmentBytes(unsafe.Pointer(&scale), unsafe.Sizeof(scale), 5)
if d.src != nil { if g.src != nil {
rce.SetFragmentTexture(d.src.texture, 0) rce.SetFragmentTexture(g.src.texture, 0)
} else { } else {
rce.SetFragmentTexture(mtl.Texture{}, 0) rce.SetFragmentTexture(mtl.Texture{}, 0)
} }
rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, indexLen, mtl.IndexTypeUInt16, d.ib, indexOffset*2) rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, indexLen, mtl.IndexTypeUInt16, g.ib, indexOffset*2)
rce.EndEncoding() rce.EndEncoding()
return nil return nil
@ -668,76 +668,76 @@ func (d *Driver) Draw(indexLen int, indexOffset int, mode driver.CompositeMode,
return nil return nil
} }
func (d *Driver) ResetSource() { func (g *Graphics) ResetSource() {
d.t.Call(func() error { g.t.Call(func() error {
d.src = nil g.src = nil
return nil return nil
}) })
} }
func (d *Driver) SetVsyncEnabled(enabled bool) { func (g *Graphics) SetVsyncEnabled(enabled bool) {
d.view.setDisplaySyncEnabled(enabled) g.view.setDisplaySyncEnabled(enabled)
} }
func (d *Driver) VDirection() driver.VDirection { func (g *Graphics) VDirection() driver.VDirection {
return driver.VUpward return driver.VUpward
} }
func (d *Driver) NeedsRestoring() bool { func (g *Graphics) NeedsRestoring() bool {
return false return false
} }
func (d *Driver) IsGL() bool { func (g *Graphics) IsGL() bool {
return false return false
} }
func (d *Driver) HasHighPrecisionFloat() bool { func (g *Graphics) HasHighPrecisionFloat() bool {
return true return true
} }
func (d *Driver) MaxImageSize() int { func (g *Graphics) MaxImageSize() int {
m := 0 m := 0
d.t.Call(func() error { g.t.Call(func() error {
if d.maxImageSize == 0 { if g.maxImageSize == 0 {
d.maxImageSize = 4096 g.maxImageSize = 4096
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
switch { switch {
case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily5_v1): case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily5_v1):
d.maxImageSize = 16384 g.maxImageSize = 16384
case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily4_v1): case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily4_v1):
d.maxImageSize = 16384 g.maxImageSize = 16384
case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily3_v1): case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily3_v1):
d.maxImageSize = 16384 g.maxImageSize = 16384
case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v2): case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v2):
d.maxImageSize = 8192 g.maxImageSize = 8192
case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v1): case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily2_v1):
d.maxImageSize = 4096 g.maxImageSize = 4096
case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v2): case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v2):
d.maxImageSize = 8192 g.maxImageSize = 8192
case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v1): case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_iOS_GPUFamily1_v1):
d.maxImageSize = 4096 g.maxImageSize = 4096
case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily2_v1): case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily2_v1):
d.maxImageSize = 16384 g.maxImageSize = 16384
case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily1_v1): case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_tvOS_GPUFamily1_v1):
d.maxImageSize = 8192 g.maxImageSize = 8192
case d.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_macOS_GPUFamily1_v1): case g.view.getMTLDevice().SupportsFeatureSet(mtl.FeatureSet_macOS_GPUFamily1_v1):
d.maxImageSize = 16384 g.maxImageSize = 16384
default: default:
panic("metal: there is no supported feature set") panic("metal: there is no supported feature set")
} }
} }
m = d.maxImageSize m = g.maxImageSize
return nil return nil
}) })
return m return m
} }
type Image struct { type Image struct {
driver *Driver graphics *Graphics
width int width int
height int height int
screen bool screen bool
texture mtl.Texture texture mtl.Texture
} }
// viewportSize must be called from the main thread. // viewportSize must be called from the main thread.
@ -749,7 +749,7 @@ func (i *Image) viewportSize() (int, int) {
} }
func (i *Image) Dispose() { func (i *Image) Dispose() {
i.driver.t.Call(func() error { i.graphics.t.Call(func() error {
if i.texture != (mtl.Texture{}) { if i.texture != (mtl.Texture{}) {
i.texture.Release() i.texture.Release()
i.texture = mtl.Texture{} i.texture = mtl.Texture{}
@ -766,12 +766,12 @@ func (i *Image) IsInvalidated() bool {
} }
func (i *Image) syncTexture() { func (i *Image) syncTexture() {
i.driver.t.Call(func() error { i.graphics.t.Call(func() error {
if i.driver.cb != (mtl.CommandBuffer{}) { if i.graphics.cb != (mtl.CommandBuffer{}) {
panic("metal: command buffer must be empty at syncTexture: flush is not called yet?") panic("metal: command buffer must be empty at syncTexture: flush is not called yet?")
} }
cb := i.driver.cq.MakeCommandBuffer() cb := i.graphics.cq.MakeCommandBuffer()
bce := cb.MakeBlitCommandEncoder() bce := cb.MakeBlitCommandEncoder()
bce.SynchronizeTexture(i.texture, 0, 0) bce.SynchronizeTexture(i.texture, 0, 0)
bce.EndEncoding() bce.EndEncoding()
@ -782,11 +782,11 @@ func (i *Image) syncTexture() {
} }
func (i *Image) Pixels() ([]byte, error) { func (i *Image) Pixels() ([]byte, error) {
i.driver.flush(true, false) i.graphics.flush(true, false)
i.syncTexture() i.syncTexture()
b := make([]byte, 4*i.width*i.height) b := make([]byte, 4*i.width*i.height)
i.driver.t.Call(func() error { i.graphics.t.Call(func() error {
i.texture.GetBytes(&b[0], uintptr(4*i.width), mtl.Region{ i.texture.GetBytes(&b[0], uintptr(4*i.width), mtl.Region{
Size: mtl.Size{Width: i.width, Height: i.height, Depth: 1}, Size: mtl.Size{Width: i.width, Height: i.height, Depth: 1},
}, 0) }, 0)
@ -796,27 +796,27 @@ func (i *Image) Pixels() ([]byte, error) {
} }
func (i *Image) SetAsDestination() { func (i *Image) SetAsDestination() {
i.driver.t.Call(func() error { i.graphics.t.Call(func() error {
i.driver.dst = i i.graphics.dst = i
return nil return nil
}) })
} }
func (i *Image) SetAsSource() { func (i *Image) SetAsSource() {
i.driver.t.Call(func() error { i.graphics.t.Call(func() error {
i.driver.src = i i.graphics.src = i
return nil return nil
}) })
} }
func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) { func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
d := i.driver g := i.graphics
if d.drawCalled { if g.drawCalled {
d.flush(true, false) g.flush(true, false)
d.drawCalled = false g.drawCalled = false
} }
d.t.Call(func() error { g.t.Call(func() error {
for _, a := range args { for _, a := range args {
i.texture.ReplaceRegion(mtl.Region{ i.texture.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},