mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +01:00
internal/cocoa, internal/graphicsdriver/metal/*: minimize allocations (#3133)
Cache the result of objc.RegisterName at program startup. Use NSInvocation less to avoid multiple C calls.
This commit is contained in:
parent
810e45c030
commit
5dee21dc7d
@ -63,6 +63,8 @@ var (
|
||||
sel_deviceDescription = objc.RegisterName("deviceDescription")
|
||||
sel_objectForKey = objc.RegisterName("objectForKey:")
|
||||
sel_unsignedIntValue = objc.RegisterName("unsignedIntValue")
|
||||
sel_setLayer = objc.RegisterName("setLayer:")
|
||||
sel_setWantsLayer = objc.RegisterName("setWantsLayer:")
|
||||
)
|
||||
|
||||
const (
|
||||
@ -147,14 +149,7 @@ func (w NSWindow) Screen() NSScreen {
|
||||
}
|
||||
|
||||
func (w NSWindow) Frame() NSRect {
|
||||
sig := NSMethodSignature_instanceMethodSignatureForSelector(objc.ID(class_NSWindow), sel_frame)
|
||||
inv := NSInvocation_invocationWithMethodSignature(sig)
|
||||
inv.SetTarget(w.ID)
|
||||
inv.SetSelector(sel_frame)
|
||||
inv.Invoke()
|
||||
var rect NSRect
|
||||
inv.GetReturnValue(unsafe.Pointer(&rect))
|
||||
return rect
|
||||
return objc.Send[NSRect](w.ID, sel_frame)
|
||||
}
|
||||
|
||||
func (w NSWindow) ContentView() NSView {
|
||||
@ -166,29 +161,19 @@ type NSView struct {
|
||||
}
|
||||
|
||||
func (v NSView) SetFrameSize(size CGSize) {
|
||||
sig := NSMethodSignature_instanceMethodSignatureForSelector(objc.ID(class_NSView), sel_setFrameSize)
|
||||
inv := NSInvocation_invocationWithMethodSignature(sig)
|
||||
inv.SetSelector(sel_setFrameSize)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(&size), 2)
|
||||
inv.InvokeWithTarget(v.ID)
|
||||
v.ID.Send(sel_setFrameSize, size)
|
||||
}
|
||||
|
||||
func (v NSView) Frame() NSRect {
|
||||
sig := NSMethodSignature_instanceMethodSignatureForSelector(objc.ID(class_NSView), sel_frame)
|
||||
inv := NSInvocation_invocationWithMethodSignature(sig)
|
||||
inv.SetSelector(sel_frame)
|
||||
inv.InvokeWithTarget(v.ID)
|
||||
var rect NSRect
|
||||
inv.GetReturnValue(unsafe.Pointer(&rect))
|
||||
return rect
|
||||
return objc.Send[NSRect](v.ID, sel_frame)
|
||||
}
|
||||
|
||||
func (v NSView) SetLayer(layer uintptr) {
|
||||
v.Send(objc.RegisterName("setLayer:"), layer)
|
||||
v.Send(sel_setLayer, layer)
|
||||
}
|
||||
|
||||
func (v NSView) SetWantsLayer(wantsLayer bool) {
|
||||
v.Send(objc.RegisterName("setWantsLayer:"), wantsLayer)
|
||||
v.Send(sel_setWantsLayer, wantsLayer)
|
||||
}
|
||||
|
||||
// NSInvocation is being used to call functions that can't be called directly with purego.SyscallN.
|
||||
@ -229,10 +214,6 @@ type NSMethodSignature struct {
|
||||
objc.ID
|
||||
}
|
||||
|
||||
func NSMethodSignature_instanceMethodSignatureForSelector(self objc.ID, cmd objc.SEL) NSMethodSignature {
|
||||
return NSMethodSignature{self.Send(sel_instanceMethodSignatureForSelector, cmd)}
|
||||
}
|
||||
|
||||
// NSMethodSignature_signatureWithObjCTypes takes a string that represents the type signature of a method.
|
||||
// It follows the encoding specified in the Apple Docs.
|
||||
//
|
||||
|
@ -33,6 +33,26 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
|
||||
)
|
||||
|
||||
var class_CAMetalLayer = objc.GetClass("CAMetalLayer")
|
||||
|
||||
var (
|
||||
sel_pixelFormat = objc.RegisterName("pixelFormat")
|
||||
sel_setDevice = objc.RegisterName("setDevice:")
|
||||
sel_setOpaque = objc.RegisterName("setOpaque:")
|
||||
sel_setPixelFormat = objc.RegisterName("setPixelFormat:")
|
||||
sel_new = objc.RegisterName("new")
|
||||
sel_setColorspace = objc.RegisterName("setColorspace:")
|
||||
sel_setMaximumDrawableCount = objc.RegisterName("setMaximumDrawableCount:")
|
||||
sel_setDisplaySyncEnabled = objc.RegisterName("setDisplaySyncEnabled:")
|
||||
sel_setDrawableSize = objc.RegisterName("setDrawableSize:")
|
||||
sel_nextDrawable = objc.RegisterName("nextDrawable")
|
||||
sel_presentsWithTransaction = objc.RegisterName("presentsWithTransaction")
|
||||
sel_setPresentsWithTransaction = objc.RegisterName("setPresentsWithTransaction:")
|
||||
sel_setFramebufferOnly = objc.RegisterName("setFramebufferOnly:")
|
||||
sel_texture = objc.RegisterName("texture")
|
||||
sel_present = objc.RegisterName("present")
|
||||
)
|
||||
|
||||
// Layer is an object that manages image-based content and
|
||||
// allows you to perform animations on that content.
|
||||
//
|
||||
@ -86,14 +106,14 @@ func NewMetalLayer(colorSpace graphicsdriver.ColorSpace) (MetalLayer, error) {
|
||||
colorSpaceSym = kCGColorSpaceDisplayP3
|
||||
}
|
||||
|
||||
layer := objc.ID(objc.GetClass("CAMetalLayer")).Send(objc.RegisterName("new"))
|
||||
layer := objc.ID(class_CAMetalLayer).Send(sel_new)
|
||||
// setColorspace: is available from iOS 13.0?
|
||||
// https://github.com/hajimehoshi/ebiten/commit/3af351a2aa31e30affd433429c42130015b302f3
|
||||
// TODO: Enable this on iOS as well.
|
||||
if runtime.GOOS != "ios" {
|
||||
// Dlsym returns pointer to symbol so dereference it.
|
||||
colorspace, _, _ := purego.SyscallN(cgColorSpaceCreateWithName, **(**uintptr)(unsafe.Pointer(&colorSpaceSym)))
|
||||
layer.Send(objc.RegisterName("setColorspace:"), colorspace)
|
||||
layer.Send(sel_setColorspace, colorspace)
|
||||
purego.SyscallN(cgColorSpaceRelease, colorspace)
|
||||
}
|
||||
return MetalLayer{layer}, nil
|
||||
@ -108,19 +128,19 @@ func (ml MetalLayer) Layer() unsafe.Pointer {
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat?language=objc.
|
||||
func (ml MetalLayer) PixelFormat() mtl.PixelFormat {
|
||||
return mtl.PixelFormat(ml.metalLayer.Send(objc.RegisterName("pixelFormat")))
|
||||
return mtl.PixelFormat(ml.metalLayer.Send(sel_pixelFormat))
|
||||
}
|
||||
|
||||
// SetDevice sets the Metal device responsible for the layer's drawable resources.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478163-device?language=objc.
|
||||
func (ml MetalLayer) SetDevice(device mtl.Device) {
|
||||
ml.metalLayer.Send(objc.RegisterName("setDevice:"), uintptr(device.Device()))
|
||||
ml.metalLayer.Send(sel_setDevice, uintptr(device.Device()))
|
||||
}
|
||||
|
||||
// SetOpaque a Boolean value indicating whether the layer contains completely opaque content.
|
||||
func (ml MetalLayer) SetOpaque(opaque bool) {
|
||||
ml.metalLayer.Send(objc.RegisterName("setOpaque:"), opaque)
|
||||
ml.metalLayer.Send(sel_setOpaque, opaque)
|
||||
}
|
||||
|
||||
// SetPixelFormat controls the pixel format of textures for rendering layer content.
|
||||
@ -136,7 +156,7 @@ func (ml MetalLayer) SetPixelFormat(pf mtl.PixelFormat) {
|
||||
default:
|
||||
panic(errors.New(fmt.Sprintf("invalid pixel format %d", pf)))
|
||||
}
|
||||
ml.metalLayer.Send(objc.RegisterName("setPixelFormat:"), uint(pf))
|
||||
ml.metalLayer.Send(sel_setPixelFormat, uint(pf))
|
||||
}
|
||||
|
||||
// SetMaximumDrawableCount controls the number of Metal drawables in the resource pool
|
||||
@ -149,7 +169,7 @@ func (ml MetalLayer) SetMaximumDrawableCount(count int) {
|
||||
if count < 2 || count > 3 {
|
||||
panic(errors.New(fmt.Sprintf("failed trying to set maximumDrawableCount to %d outside of the valid range of [2, 3]", count)))
|
||||
}
|
||||
ml.metalLayer.Send(objc.RegisterName("setMaximumDrawableCount:"), count)
|
||||
ml.metalLayer.Send(sel_setMaximumDrawableCount, count)
|
||||
}
|
||||
|
||||
// SetDisplaySyncEnabled controls whether the Metal layer and its drawables
|
||||
@ -160,28 +180,21 @@ func (ml MetalLayer) SetDisplaySyncEnabled(enabled bool) {
|
||||
if runtime.GOOS == "ios" {
|
||||
return
|
||||
}
|
||||
ml.metalLayer.Send(objc.RegisterName("setDisplaySyncEnabled:"), enabled)
|
||||
ml.metalLayer.Send(sel_setDisplaySyncEnabled, enabled)
|
||||
}
|
||||
|
||||
// SetDrawableSize sets the size, in pixels, of textures for rendering layer content.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478174-drawablesize?language=objc.
|
||||
func (ml MetalLayer) SetDrawableSize(width, height int) {
|
||||
// TODO: once objc supports calling functions with struct arguments replace this with just a ID.Send call
|
||||
var sel_setDrawableSize = objc.RegisterName("setDrawableSize:")
|
||||
sig := cocoa.NSMethodSignature_instanceMethodSignatureForSelector(objc.ID(objc.GetClass("CAMetalLayer")), sel_setDrawableSize)
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(sig)
|
||||
inv.SetTarget(ml.metalLayer)
|
||||
inv.SetSelector(sel_setDrawableSize)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(&cocoa.CGSize{Width: cocoa.CGFloat(width), Height: cocoa.CGFloat(height)}), 2)
|
||||
inv.Invoke()
|
||||
ml.metalLayer.Send(sel_setDrawableSize, cocoa.CGSize{Width: cocoa.CGFloat(width), Height: cocoa.CGFloat(height)})
|
||||
}
|
||||
|
||||
// NextDrawable returns a Metal drawable.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478172-nextdrawable?language=objc.
|
||||
func (ml MetalLayer) NextDrawable() (MetalDrawable, error) {
|
||||
md := ml.metalLayer.Send(objc.RegisterName("nextDrawable"))
|
||||
md := ml.metalLayer.Send(sel_nextDrawable)
|
||||
if md == 0 {
|
||||
return MetalDrawable{}, errors.New("nextDrawable returned nil")
|
||||
}
|
||||
@ -192,21 +205,21 @@ func (ml MetalLayer) NextDrawable() (MetalDrawable, error) {
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction?language=objc
|
||||
func (ml MetalLayer) PresentsWithTransaction() bool {
|
||||
return ml.metalLayer.Send(objc.RegisterName("presentsWithTransaction")) != 0
|
||||
return ml.metalLayer.Send(sel_presentsWithTransaction) != 0
|
||||
}
|
||||
|
||||
// SetPresentsWithTransaction sets a Boolean value that determines whether the layer presents its content using a Core Animation transaction.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction?language=objc
|
||||
func (ml MetalLayer) SetPresentsWithTransaction(presentsWithTransaction bool) {
|
||||
ml.metalLayer.Send(objc.RegisterName("setPresentsWithTransaction:"), presentsWithTransaction)
|
||||
ml.metalLayer.Send(sel_setPresentsWithTransaction, presentsWithTransaction)
|
||||
}
|
||||
|
||||
// SetFramebufferOnly sets a Boolean value that determines whether the layer’s textures are used only for rendering.
|
||||
//
|
||||
// https://developer.apple.com/documentation/quartzcore/cametallayer/1478168-framebufferonly?language=objc
|
||||
func (ml MetalLayer) SetFramebufferOnly(framebufferOnly bool) {
|
||||
ml.metalLayer.Send(objc.RegisterName("setFramebufferOnly:"), framebufferOnly)
|
||||
ml.metalLayer.Send(sel_setFramebufferOnly, framebufferOnly)
|
||||
}
|
||||
|
||||
// MetalDrawable is a displayable resource that can be rendered or written to by Metal.
|
||||
@ -225,12 +238,12 @@ func (md MetalDrawable) Drawable() unsafe.Pointer {
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture?language=objc.
|
||||
func (md MetalDrawable) Texture() mtl.Texture {
|
||||
return mtl.NewTexture(md.metalDrawable.Send(objc.RegisterName("texture")))
|
||||
return mtl.NewTexture(md.metalDrawable.Send(sel_texture))
|
||||
}
|
||||
|
||||
// Present presents the drawable onscreen as soon as possible.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldrawable/1470284-present?language=objc.
|
||||
func (md MetalDrawable) Present() {
|
||||
md.metalDrawable.Send(objc.RegisterName("present"))
|
||||
md.metalDrawable.Send(sel_present)
|
||||
}
|
||||
|
@ -32,6 +32,8 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
||||
)
|
||||
|
||||
var sel_supportsFamily = objc.RegisterName("supportsFamily:")
|
||||
|
||||
type Graphics struct {
|
||||
view view
|
||||
|
||||
@ -709,7 +711,7 @@ func (g *Graphics) MaxImageSize() int {
|
||||
|
||||
// supportsFamily is available as of macOS 10.15+ and iOS 13.0+.
|
||||
// https://developer.apple.com/documentation/metal/mtldevice/3143473-supportsfamily
|
||||
if d.RespondsToSelector(objc.RegisterName("supportsFamily:")) {
|
||||
if d.RespondsToSelector(sel_supportsFamily) {
|
||||
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
|
||||
g.maxImageSize = 8192
|
||||
switch {
|
||||
|
@ -484,7 +484,6 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
sel_class = objc.RegisterName("class")
|
||||
sel_length = objc.RegisterName("length")
|
||||
sel_isHeadless = objc.RegisterName("isHeadless")
|
||||
sel_isLowPower = objc.RegisterName("isLowPower")
|
||||
@ -853,12 +852,7 @@ func (cb CommandBuffer) RenderCommandEncoderWithDescriptor(rpd RenderPassDescrip
|
||||
colorAttachments0.Send(sel_setLoadAction, int(rpd.ColorAttachments[0].LoadAction))
|
||||
colorAttachments0.Send(sel_setStoreAction, int(rpd.ColorAttachments[0].StoreAction))
|
||||
colorAttachments0.Send(sel_setTexture, rpd.ColorAttachments[0].Texture.texture)
|
||||
sig := cocoa.NSMethodSignature_instanceMethodSignatureForSelector(colorAttachments0.Send(sel_class), sel_setClearColor)
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(sig)
|
||||
inv.SetTarget(colorAttachments0)
|
||||
inv.SetSelector(sel_setClearColor)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(&rpd.ColorAttachments[0].ClearColor), 2)
|
||||
inv.Invoke()
|
||||
colorAttachments0.Send(sel_setClearColor, rpd.ColorAttachments[0].ClearColor)
|
||||
var stencilAttachment = renderPassDescriptor.Send(sel_stencilAttachment)
|
||||
stencilAttachment.Send(sel_setLoadAction, int(rpd.StencilAttachment.LoadAction))
|
||||
stencilAttachment.Send(sel_setStoreAction, int(rpd.StencilAttachment.StoreAction))
|
||||
@ -912,11 +906,7 @@ func (rce RenderCommandEncoder) SetRenderPipelineState(rps RenderPipelineState)
|
||||
}
|
||||
|
||||
func (rce RenderCommandEncoder) SetViewport(viewport Viewport) {
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:{MTLViewport=dddddd}"))
|
||||
inv.SetTarget(rce.commandEncoder)
|
||||
inv.SetSelector(sel_setViewport)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(&viewport), 2)
|
||||
inv.Invoke()
|
||||
rce.commandEncoder.Send(sel_setViewport, viewport)
|
||||
}
|
||||
|
||||
// SetScissorRect sets the scissor rectangle for a fragment scissor test.
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego/objc"
|
||||
|
||||
@ -257,13 +256,7 @@ var (
|
||||
)
|
||||
|
||||
func currentMouseLocation() (x, y int) {
|
||||
sig := cocoa.NSMethodSignature_signatureWithObjCTypes("{NSPoint=dd}@:")
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(sig)
|
||||
inv.SetTarget(objc.ID(class_NSEvent))
|
||||
inv.SetSelector(sel_mouseLocation)
|
||||
inv.Invoke()
|
||||
var point cocoa.NSPoint
|
||||
inv.GetReturnValue(unsafe.Pointer(&point))
|
||||
point := objc.Send[cocoa.NSPoint](objc.ID(class_NSEvent), sel_mouseLocation)
|
||||
|
||||
x, y = int(point.X), int(point.Y)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user