ebiten/internal/graphicsdriver/metal/ca/ca_darwin.go
TotallyGamerJet 5dee21dc7d
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.
2024-10-18 11:44:37 +09:00

250 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2018 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package ca provides access to Apple's Core Animation API (https://developer.apple.com/documentation/quartzcore).
//
// This package is in very early stages of development.
// It's a minimal implementation with scope limited to
// supporting the movingtriangle example.
package ca
import (
"errors"
"fmt"
"runtime"
"unsafe"
"github.com/ebitengine/purego"
"github.com/ebitengine/purego/objc"
"github.com/hajimehoshi/ebiten/v2/internal/cocoa"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"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.
//
// Reference: https://developer.apple.com/documentation/quartzcore/calayer?language=objc.
type Layer interface {
// Layer returns the underlying CALayer * pointer.
Layer() unsafe.Pointer
}
// MetalLayer is a Core Animation Metal layer, a layer that manages a pool of Metal drawables.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc.
type MetalLayer struct {
metalLayer objc.ID
}
// NewMetalLayer creates a new Core Animation Metal layer.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc.
func NewMetalLayer(colorSpace graphicsdriver.ColorSpace) (MetalLayer, error) {
coreGraphics, err := purego.Dlopen("/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
if err != nil {
return MetalLayer{}, err
}
cgColorSpaceCreateWithName, err := purego.Dlsym(coreGraphics, "CGColorSpaceCreateWithName")
if err != nil {
return MetalLayer{}, err
}
cgColorSpaceRelease, err := purego.Dlsym(coreGraphics, "CGColorSpaceRelease")
if err != nil {
return MetalLayer{}, err
}
var colorSpaceSym uintptr
switch colorSpace {
case graphicsdriver.ColorSpaceSRGB:
kCGColorSpaceSRGB, err := purego.Dlsym(coreGraphics, "kCGColorSpaceSRGB")
if err != nil {
return MetalLayer{}, err
}
colorSpaceSym = kCGColorSpaceSRGB
default:
fallthrough
case graphicsdriver.ColorSpaceDisplayP3:
kCGColorSpaceDisplayP3, err := purego.Dlsym(coreGraphics, "kCGColorSpaceDisplayP3")
if err != nil {
return MetalLayer{}, err
}
colorSpaceSym = kCGColorSpaceDisplayP3
}
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(sel_setColorspace, colorspace)
purego.SyscallN(cgColorSpaceRelease, colorspace)
}
return MetalLayer{layer}, nil
}
// Layer implements the Layer interface.
func (ml MetalLayer) Layer() unsafe.Pointer {
return *(*unsafe.Pointer)(unsafe.Pointer(&ml.metalLayer))
}
// PixelFormat returns the pixel format of textures for rendering layer content.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat?language=objc.
func (ml MetalLayer) PixelFormat() mtl.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(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(sel_setOpaque, opaque)
}
// SetPixelFormat controls the pixel format of textures for rendering layer content.
//
// The pixel format for a Metal layer must be PixelFormatBGRA8UNorm, PixelFormatBGRA8UNormSRGB,
// PixelFormatRGBA16Float, PixelFormatBGRA10XR, or PixelFormatBGRA10XRSRGB.
// SetPixelFormat panics for other values.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat?language=objc.
func (ml MetalLayer) SetPixelFormat(pf mtl.PixelFormat) {
switch pf {
case mtl.PixelFormatRGBA8UNorm, mtl.PixelFormatRGBA8UNormSRGB, mtl.PixelFormatBGRA8UNorm, mtl.PixelFormatBGRA8UNormSRGB, mtl.PixelFormatStencil8:
default:
panic(errors.New(fmt.Sprintf("invalid pixel format %d", pf)))
}
ml.metalLayer.Send(sel_setPixelFormat, uint(pf))
}
// SetMaximumDrawableCount controls the number of Metal drawables in the resource pool
// managed by Core Animation.
//
// It can set to 2 or 3 only. SetMaximumDrawableCount panics for other values.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2938720-maximumdrawablecount?language=objc.
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(sel_setMaximumDrawableCount, count)
}
// SetDisplaySyncEnabled controls whether the Metal layer and its drawables
// are synchronized with the display's refresh rate.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2887087-displaysyncenabled?language=objc.
func (ml MetalLayer) SetDisplaySyncEnabled(enabled bool) {
if runtime.GOOS == "ios" {
return
}
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) {
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(sel_nextDrawable)
if md == 0 {
return MetalDrawable{}, errors.New("nextDrawable returned nil")
}
return MetalDrawable{md}, nil
}
// PresentsWithTransaction returns 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) PresentsWithTransaction() bool {
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(sel_setPresentsWithTransaction, presentsWithTransaction)
}
// SetFramebufferOnly sets a Boolean value that determines whether the layers 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(sel_setFramebufferOnly, framebufferOnly)
}
// MetalDrawable is a displayable resource that can be rendered or written to by Metal.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable?language=objc.
type MetalDrawable struct {
metalDrawable objc.ID
}
// Drawable implements the mtl.Drawable interface.
func (md MetalDrawable) Drawable() unsafe.Pointer {
return *(*unsafe.Pointer)(unsafe.Pointer(&md.metalDrawable))
}
// Texture returns a Metal texture object representing the drawable object's content.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture?language=objc.
func (md MetalDrawable) Texture() mtl.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(sel_present)
}