2018-11-12 16:00:10 +01:00
// 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"
2022-08-17 18:39:34 +02:00
"fmt"
2022-09-14 18:01:25 +02:00
"runtime"
2018-11-12 16:00:10 +01:00
"unsafe"
2022-08-17 18:39:34 +02:00
"github.com/ebitengine/purego"
"github.com/ebitengine/purego/objc"
"github.com/hajimehoshi/ebiten/v2/internal/cocoa"
2020-10-03 19:35:13 +02:00
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
2018-11-12 16:00:10 +01:00
)
// 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.
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.
type MetalLayer struct {
2022-08-17 18:39:34 +02:00
metalLayer objc . ID
2018-11-12 16:00:10 +01:00
}
// MakeMetalLayer creates a new Core Animation Metal layer.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
2023-03-03 17:08:08 +01:00
func MakeMetalLayer ( ) ( MetalLayer , error ) {
2023-03-10 15:24:03 +01:00
coreGraphics , err := purego . Dlopen ( "CoreGraphics.framework/CoreGraphics" , purego . RTLD_LAZY | purego . RTLD_GLOBAL )
2023-03-03 17:08:08 +01:00
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
}
kCGColorSpaceDisplayP3 , err := purego . Dlsym ( coreGraphics , "kCGColorSpaceDisplayP3" )
if err != nil {
return MetalLayer { } , err
}
2022-08-17 18:39:34 +02:00
layer := objc . ID ( objc . GetClass ( "CAMetalLayer" ) ) . Send ( objc . RegisterName ( "new" ) )
2022-09-14 18:01:25 +02:00
if runtime . GOOS != "ios" {
2023-03-03 17:08:08 +01:00
colorspace , _ , _ := purego . SyscallN ( cgColorSpaceCreateWithName , * * ( * * uintptr ) ( unsafe . Pointer ( & kCGColorSpaceDisplayP3 ) ) ) // Dlsym returns pointer to symbol so dereference it
2022-08-17 18:39:34 +02:00
layer . Send ( objc . RegisterName ( "setColorspace:" ) , colorspace )
2023-03-03 17:08:08 +01:00
purego . SyscallN ( cgColorSpaceRelease , colorspace )
2022-08-17 18:39:34 +02:00
}
2023-03-03 17:08:08 +01:00
return MetalLayer { layer } , nil
2018-11-12 16:00:10 +01:00
}
// Layer implements the Layer interface.
2022-08-17 18:39:34 +02:00
func ( ml MetalLayer ) Layer ( ) unsafe . Pointer {
return * ( * unsafe . Pointer ) ( unsafe . Pointer ( & ml . metalLayer ) )
}
2018-11-12 16:00:10 +01:00
// PixelFormat returns the pixel format of textures for rendering layer content.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
func ( ml MetalLayer ) PixelFormat ( ) mtl . PixelFormat {
2022-08-17 18:39:34 +02:00
return mtl . PixelFormat ( ml . metalLayer . Send ( objc . RegisterName ( "pixelFormat" ) ) )
2018-11-12 16:00:10 +01:00
}
// SetDevice sets the Metal device responsible for the layer's drawable resources.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478163-device.
func ( ml MetalLayer ) SetDevice ( device mtl . Device ) {
2022-08-17 18:39:34 +02:00
ml . metalLayer . Send ( objc . RegisterName ( "setDevice:" ) , uintptr ( device . Device ( ) ) )
2018-11-12 16:00:10 +01:00
}
2019-11-30 16:07:41 +01:00
// SetOpaque a Boolean value indicating whether the layer contains completely opaque content.
func ( ml MetalLayer ) SetOpaque ( opaque bool ) {
2022-08-17 18:39:34 +02:00
ml . metalLayer . Send ( objc . RegisterName ( "setOpaque:" ) , opaque )
2019-11-30 16:07:41 +01:00
}
2018-11-12 16:00:10 +01:00
// 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.
func ( ml MetalLayer ) SetPixelFormat ( pf mtl . PixelFormat ) {
2022-08-17 18:39:34 +02:00
switch pf {
case mtl . PixelFormatRGBA8UNorm , mtl . PixelFormatRGBA8UNormSRGB , mtl . PixelFormatBGRA8UNorm , mtl . PixelFormatBGRA8UNormSRGB , mtl . PixelFormatStencil8 :
default :
panic ( errors . New ( fmt . Sprintf ( "invalid pixel format %d" , pf ) ) )
2018-11-12 16:00:10 +01:00
}
2022-08-17 18:39:34 +02:00
ml . metalLayer . Send ( objc . RegisterName ( "setPixelFormat:" ) , uint ( pf ) )
2018-11-12 16:00:10 +01:00
}
// 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.
func ( ml MetalLayer ) SetMaximumDrawableCount ( count int ) {
2022-08-17 18:39:34 +02:00
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 ) ) )
2018-11-12 16:00:10 +01:00
}
2022-08-17 18:39:34 +02:00
ml . metalLayer . Send ( objc . RegisterName ( "setMaximumDrawableCount:" ) , count )
2018-11-12 16:00:10 +01:00
}
// 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.
func ( ml MetalLayer ) SetDisplaySyncEnabled ( enabled bool ) {
2022-09-14 18:01:25 +02:00
if runtime . GOOS == "ios" {
2022-08-17 18:39:34 +02:00
return
2018-11-12 16:00:10 +01:00
}
2022-08-17 18:39:34 +02:00
ml . metalLayer . Send ( objc . RegisterName ( "setDisplaySyncEnabled:" ) , enabled )
2018-11-12 16:00:10 +01:00
}
// SetDrawableSize sets the size, in pixels, of textures for rendering layer content.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478174-drawablesize.
func ( ml MetalLayer ) SetDrawableSize ( width , height int ) {
2022-08-17 18:39:34 +02:00
// 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 ( )
2018-11-12 16:00:10 +01:00
}
// NextDrawable returns a Metal drawable.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478172-nextdrawable.
func ( ml MetalLayer ) NextDrawable ( ) ( MetalDrawable , error ) {
2022-08-17 18:39:34 +02:00
md := ml . metalLayer . Send ( objc . RegisterName ( "nextDrawable" ) )
if md == 0 {
2018-11-12 16:00:10 +01:00
return MetalDrawable { } , errors . New ( "nextDrawable returned nil" )
}
return MetalDrawable { md } , nil
}
2021-07-07 21:21:20 +02:00
// 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
func ( ml MetalLayer ) PresentsWithTransaction ( ) bool {
2022-08-17 18:39:34 +02:00
return ml . metalLayer . Send ( objc . RegisterName ( "presentsWithTransaction" ) ) != 0
2021-07-07 21:21:20 +02:00
}
2021-08-08 08:13:44 +02:00
// 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
func ( ml MetalLayer ) SetPresentsWithTransaction ( presentsWithTransaction bool ) {
2022-08-17 18:39:34 +02:00
ml . metalLayer . Send ( objc . RegisterName ( "setPresentsWithTransaction:" ) , presentsWithTransaction )
2021-08-08 08:13:44 +02:00
}
2021-07-06 17:38:50 +02:00
// 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
func ( ml MetalLayer ) SetFramebufferOnly ( framebufferOnly bool ) {
2022-08-17 18:39:34 +02:00
ml . metalLayer . Send ( objc . RegisterName ( "setFramebufferOnly:" ) , framebufferOnly )
2021-07-06 17:38:50 +02:00
}
2018-11-12 16:00:10 +01:00
// MetalDrawable is a displayable resource that can be rendered or written to by Metal.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable.
type MetalDrawable struct {
2022-08-17 18:39:34 +02:00
metalDrawable objc . ID
2018-11-12 16:00:10 +01:00
}
// Drawable implements the mtl.Drawable interface.
2022-08-17 18:39:34 +02:00
func ( md MetalDrawable ) Drawable ( ) unsafe . Pointer {
return * ( * unsafe . Pointer ) ( unsafe . Pointer ( & md . metalDrawable ) )
}
2018-11-12 16:00:10 +01:00
// Texture returns a Metal texture object representing the drawable object's content.
//
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture.
func ( md MetalDrawable ) Texture ( ) mtl . Texture {
2022-08-17 18:39:34 +02:00
return mtl . NewTexture ( md . metalDrawable . Send ( objc . RegisterName ( "texture" ) ) )
2018-11-12 16:00:10 +01:00
}
2021-07-07 21:21:20 +02:00
// Present presents the drawable onscreen as soon as possible.
//
// Reference: https://developer.apple.com/documentation/metal/mtldrawable/1470284-present.
func ( md MetalDrawable ) Present ( ) {
2022-08-17 18:39:34 +02:00
md . metalDrawable . Send ( objc . RegisterName ( "present" ) )
2021-07-07 21:21:20 +02:00
}