updated ebiten version from 2.7.9 to 2.9.9
This commit is contained in:
Generated
Vendored
+150
-46
@@ -29,13 +29,46 @@ import (
|
||||
"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")
|
||||
class_CAMetalDisplayLink = objc.GetClass("CAMetalDisplayLink")
|
||||
class_CAMetalDisplayLinkUpdate = objc.GetClass("CAMetalDisplayLinkUpdate")
|
||||
)
|
||||
|
||||
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")
|
||||
sel_alloc = objc.RegisterName("alloc")
|
||||
sel_initWithMetalLayer = objc.RegisterName("initWithMetalLayer:")
|
||||
sel_setDelegate = objc.RegisterName("setDelegate:")
|
||||
sel_addToOneLoopForMode = objc.RegisterName("addToRunLoop:forMode:")
|
||||
sel_removeFromRunLoopForMode = objc.RegisterName("removeFromRunLoop:forMode:")
|
||||
sel_setPaused = objc.RegisterName("setPaused:")
|
||||
sel_drawable = objc.RegisterName("drawable")
|
||||
sel_release = objc.RegisterName("release")
|
||||
)
|
||||
|
||||
// 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.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/calayer?language=objc.
|
||||
type Layer interface {
|
||||
// Layer returns the underlying CALayer * pointer.
|
||||
Layer() unsafe.Pointer
|
||||
@@ -43,15 +76,15 @@ type Layer interface {
|
||||
|
||||
// MetalLayer is a Core Animation Metal layer, a layer that manages a pool of Metal drawables.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer?language=objc.
|
||||
type MetalLayer struct {
|
||||
metalLayer objc.ID
|
||||
}
|
||||
|
||||
// MakeMetalLayer creates a new Core Animation Metal layer.
|
||||
// NewMetalLayer creates a new Core Animation Metal layer.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer.
|
||||
func MakeMetalLayer() (MetalLayer, error) {
|
||||
// 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
|
||||
@@ -67,15 +100,32 @@ func MakeMetalLayer() (MetalLayer, error) {
|
||||
return MetalLayer{}, err
|
||||
}
|
||||
|
||||
kCGColorSpaceDisplayP3, err := purego.Dlsym(coreGraphics, "kCGColorSpaceDisplayP3")
|
||||
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(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" {
|
||||
colorspace, _, _ := purego.SyscallN(cgColorSpaceCreateWithName, **(**uintptr)(unsafe.Pointer(&kCGColorSpaceDisplayP3))) // Dlsym returns pointer to symbol so dereference it
|
||||
layer.Send(objc.RegisterName("setColorspace:"), colorspace)
|
||||
// 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
|
||||
@@ -88,21 +138,21 @@ func (ml MetalLayer) Layer() unsafe.Pointer {
|
||||
|
||||
// PixelFormat returns the pixel format of textures for rendering layer content.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
|
||||
// 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.
|
||||
// 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.
|
||||
@@ -111,14 +161,14 @@ func (ml MetalLayer) SetOpaque(opaque bool) {
|
||||
// PixelFormatRGBA16Float, PixelFormatBGRA10XR, or PixelFormatBGRA10XRSRGB.
|
||||
// SetPixelFormat panics for other values.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/1478155-pixelformat.
|
||||
// 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)))
|
||||
panic(fmt.Sprintf("ca: 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
|
||||
@@ -126,44 +176,37 @@ func (ml MetalLayer) SetPixelFormat(pf mtl.PixelFormat) {
|
||||
//
|
||||
// It can set to 2 or 3 only. SetMaximumDrawableCount panics for other values.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2938720-maximumdrawablecount.
|
||||
// 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)))
|
||||
panic(fmt.Sprintf("ca: 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
|
||||
// are synchronized with the display's refresh rate.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametallayer/2887087-displaysyncenabled.
|
||||
// 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(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.
|
||||
// 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.
|
||||
// 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")
|
||||
}
|
||||
@@ -172,28 +215,28 @@ func (ml MetalLayer) NextDrawable() (MetalDrawable, error) {
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
// Reference: 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.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable.
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable?language=objc.
|
||||
type MetalDrawable struct {
|
||||
metalDrawable objc.ID
|
||||
}
|
||||
@@ -205,14 +248,75 @@ func (md MetalDrawable) Drawable() unsafe.Pointer {
|
||||
|
||||
// Texture returns a Metal texture object representing the drawable object's content.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldrawable/1478159-texture.
|
||||
// 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.
|
||||
// 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)
|
||||
}
|
||||
|
||||
// MetalDisplayLink is a class your Metal app uses to register for callbacks to synchronize its animations for a display.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldisplaylink?language=objc
|
||||
type MetalDisplayLink struct {
|
||||
objc.ID
|
||||
}
|
||||
|
||||
// SetDelegate sets an instance of a type your app implements that responds to the system’s callbacks.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldisplaylink/delegate?language=objc
|
||||
func (m MetalDisplayLink) SetDelegate(delegate objc.ID) {
|
||||
m.Send(sel_setDelegate, delegate)
|
||||
}
|
||||
|
||||
// AddToRunLoop registers the display link with a run loop.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldisplaylink/add(to:formode:)?language=objc
|
||||
func (m MetalDisplayLink) AddToRunLoop(runLoop cocoa.NSRunLoop, mode cocoa.NSRunLoopMode) {
|
||||
m.Send(sel_addToOneLoopForMode, runLoop, mode)
|
||||
}
|
||||
|
||||
// RemoveFromRunLoop removes a mode’s display link from a run loop.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldisplaylink/remove(from:formode:)?language=objc
|
||||
func (m MetalDisplayLink) RemoveFromRunLoop(runLoop cocoa.NSRunLoop, mode cocoa.NSRunLoopMode) {
|
||||
m.Send(sel_removeFromRunLoopForMode, runLoop, mode)
|
||||
}
|
||||
|
||||
// SetPaused sets a Boolean value that indicates whether the system suspends the display link’s notifications to the target.
|
||||
//
|
||||
// https://developer.apple.com/documentation/quartzcore/cametaldisplaylink/ispaused?language=objc
|
||||
func (m MetalDisplayLink) SetPaused(paused bool) {
|
||||
m.Send(sel_setPaused, paused)
|
||||
}
|
||||
|
||||
func (m MetalDisplayLink) Release() {
|
||||
m.Send(sel_release)
|
||||
}
|
||||
|
||||
// NewMetalDisplayLink creates a display link for Metal from a Core Animation layer.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldisplaylink/init(metallayer:)?language=objc
|
||||
func NewMetalDisplayLink(metalLayer MetalLayer) MetalDisplayLink {
|
||||
displayLink := objc.ID(class_CAMetalDisplayLink).Send(sel_alloc).Send(sel_initWithMetalLayer, metalLayer.metalLayer)
|
||||
return MetalDisplayLink{displayLink}
|
||||
}
|
||||
|
||||
// MetalDisplayLinkUpdate stores information about a single update from a Metal display link instance.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/quartzcore/cametaldisplaylink/update?language=objc
|
||||
type MetalDisplayLinkUpdate struct {
|
||||
objc.ID
|
||||
}
|
||||
|
||||
// Drawable returns the Metal drawable your app uses to render the next frame.
|
||||
//
|
||||
// https://developer.apple.com/documentation/quartzcore/cametaldisplaylink/update/drawable?language=objc
|
||||
func (m MetalDisplayLinkUpdate) Drawable() MetalDrawable {
|
||||
return MetalDrawable{m.Send(sel_drawable)}
|
||||
}
|
||||
|
||||
Generated
Vendored
+208
@@ -0,0 +1,208 @@
|
||||
// Copyright 2025 The Ebitengine 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.
|
||||
|
||||
//go:build darwin && !ios
|
||||
|
||||
package metal
|
||||
|
||||
// #cgo CFLAGS: -x objective-c
|
||||
//
|
||||
// #include <Foundation/Foundation.h>
|
||||
// #include <CoreVideo/CVDisplayLink.h>
|
||||
// #if __has_include(<QuartzCore/CAMetalLayer.h>)
|
||||
// #include <QuartzCore/CAMetalLayer.h>
|
||||
// #endif
|
||||
//
|
||||
// #cgo noescape isCAMetalDisplayLinkAvailable
|
||||
// #cgo nocallback isCAMetalDisplayLinkAvailable
|
||||
// static bool isCAMetalDisplayLinkAvailable() {
|
||||
// // TODO: Use PureGo if returning a struct is supported (ebitengine/purego#225).
|
||||
// // As operatingSystemVersion returns a struct, this cannot be written with PureGo.
|
||||
// NSOperatingSystemVersion version = [[NSProcessInfo processInfo] operatingSystemVersion];
|
||||
// if (version.majorVersion >= 14) {
|
||||
// // Also check if the CAMetalDisplayLink class exists
|
||||
// return NSClassFromString(@"CAMetalDisplayLink") != nil;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// int ebitengine_DisplayLinkOutputCallback(CVDisplayLinkRef displayLinkRef, CVTimeStamp* inNow, CVTimeStamp* inOutputTime, uint64_t flagsIn, uint64_t* flagsOut, void* displayLinkContext);
|
||||
import "C"
|
||||
import (
|
||||
"log/slog"
|
||||
"runtime"
|
||||
"runtime/cgo"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego/objc"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/cocoa"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/ca"
|
||||
)
|
||||
|
||||
func (v *view) initDisplayLink() error {
|
||||
if C.isCAMetalDisplayLinkAvailable() {
|
||||
if err := v.initCAMetalDisplayLink(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := v.initCADisplayLink(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var class_EbitengineCAMetalDisplayLinkDelegate objc.Class
|
||||
|
||||
func (v *view) initCAMetalDisplayLink() error {
|
||||
v.drawableCh = make(chan ca.MetalDrawable)
|
||||
v.drawableDoneCh = make(chan struct{})
|
||||
v.metalDisplayLinkRunLoop = createThreadWithRunLoop()
|
||||
|
||||
c, err := objc.RegisterClass(
|
||||
"EbitengineCAMetalDisplayLinkDelegate",
|
||||
objc.GetClass("NSObject"),
|
||||
[]*objc.Protocol{objc.GetProtocol("CAMetalDisplayLinkDelegate")},
|
||||
nil,
|
||||
[]objc.MethodDef{
|
||||
{
|
||||
Cmd: objc.RegisterName("metalDisplayLink:needsUpdate:"),
|
||||
Fn: func(id objc.ID, cmd objc.SEL, metalDisplayLink objc.ID, needsUpdate objc.ID) {
|
||||
// There is a case where this callback is invoked from the main run loop (#3353).
|
||||
// This is very mysterious, but this causes a deadlock.
|
||||
// As a workaround, return this immediately when the current run loop is the main run loop.
|
||||
if cocoa.NSRunLoop_currentRunLoop() == cocoa.NSRunLoop_mainRunLoop() {
|
||||
slog.Debug("metal: metalDisplayLink:needsUpdate: is unexpectedly called from the main run loop")
|
||||
return
|
||||
}
|
||||
drawable := ca.MetalDisplayLinkUpdate{ID: needsUpdate}.Drawable()
|
||||
if drawable == (ca.MetalDrawable{}) {
|
||||
return
|
||||
}
|
||||
v.drawableCh <- drawable
|
||||
<-v.drawableDoneCh
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
class_EbitengineCAMetalDisplayLinkDelegate = c
|
||||
|
||||
v.createCAMetalDisplayLink()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *view) createCAMetalDisplayLink() {
|
||||
ch := make(chan uintptr)
|
||||
v.metalDisplayLinkRunLoop.PerformBlock(objc.NewBlock(func(block objc.Block) {
|
||||
dl := ca.NewMetalDisplayLink(v.ml)
|
||||
dl.SetDelegate(objc.ID(class_EbitengineCAMetalDisplayLinkDelegate).Send(objc.RegisterName("new")))
|
||||
dl.AddToRunLoop(v.metalDisplayLinkRunLoop, cocoa.NSDefaultRunLoopMode)
|
||||
dl.SetPaused(false)
|
||||
ch <- uintptr(dl.ID)
|
||||
close(ch)
|
||||
}))
|
||||
v.metalDisplayLink = <-ch
|
||||
}
|
||||
|
||||
func createThreadWithRunLoop() cocoa.NSRunLoop {
|
||||
ch := make(chan cocoa.NSRunLoop)
|
||||
go func() {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
runLoop := cocoa.NSRunLoop_currentRunLoop()
|
||||
ch <- runLoop
|
||||
close(ch)
|
||||
|
||||
// Add a dummy mach port to keep alive.
|
||||
port := cocoa.NSMachPort_port()
|
||||
runLoop.AddPort(port, cocoa.NSRunLoopCommonModes)
|
||||
|
||||
runLoop.Run()
|
||||
}()
|
||||
|
||||
runLoop := <-ch
|
||||
if runLoop.ID == 0 {
|
||||
panic("metal: runLoop must be initialized")
|
||||
}
|
||||
return runLoop
|
||||
}
|
||||
|
||||
func (v *view) initCADisplayLink() error {
|
||||
v.fence = newFence()
|
||||
|
||||
// TODO: CVDisplayLink APIs are deprecated in macOS 10.15 and later.
|
||||
// Use new APIs like NSView.displayLink(target:selector:).
|
||||
var displayLinkRef C.CVDisplayLinkRef
|
||||
if ret := C.CVDisplayLinkCreateWithActiveCGDisplays(&displayLinkRef); ret != kCVReturnSuccess {
|
||||
// Failed to get the display link, so proceed without it.
|
||||
return nil
|
||||
}
|
||||
v.handleToSelf = cgo.NewHandle(v)
|
||||
C.CVDisplayLinkSetOutputCallback(displayLinkRef, C.CVDisplayLinkOutputCallback(C.ebitengine_DisplayLinkOutputCallback), unsafe.Pointer(&v.handleToSelf))
|
||||
C.CVDisplayLinkStart(displayLinkRef)
|
||||
|
||||
v.caDisplayLink = uintptr(displayLinkRef)
|
||||
return nil
|
||||
}
|
||||
|
||||
//export ebitengine_DisplayLinkOutputCallback
|
||||
func ebitengine_DisplayLinkOutputCallback(displayLinkRef C.CVDisplayLinkRef, inNow, inOutputTime *C.CVTimeStamp, flagsIn C.uint64_t, flagsOut *C.uint64_t, displayLinkContext unsafe.Pointer) C.int {
|
||||
cgoHandle := (*cgo.Handle)(displayLinkContext)
|
||||
view := cgoHandle.Value().(*view)
|
||||
view.fence.advance()
|
||||
return 0
|
||||
}
|
||||
|
||||
func (v *view) nextDrawable() ca.MetalDrawable {
|
||||
if v.metalDisplayLink != 0 {
|
||||
const wait = 100 * time.Millisecond
|
||||
if v.drawableTimer == nil {
|
||||
v.drawableTimer = time.NewTimer(wait)
|
||||
} else {
|
||||
v.drawableTimer.Reset(wait)
|
||||
}
|
||||
defer v.drawableTimer.Stop()
|
||||
select {
|
||||
case d := <-v.drawableCh:
|
||||
return d
|
||||
case <-v.drawableTimer.C:
|
||||
// This happens when the main thread needs to execute the notification observer callback,
|
||||
// or when the appliation goes to full screen (#3354).
|
||||
return ca.MetalDrawable{}
|
||||
}
|
||||
}
|
||||
|
||||
v.waitForDisplayLinkOutputCallback()
|
||||
|
||||
d, err := v.ml.NextDrawable()
|
||||
if err != nil {
|
||||
// Drawable is nil. This can happen at the initial state. Let's wait and see.
|
||||
return ca.MetalDrawable{}
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func (v *view) finishDrawableUsage() {
|
||||
if v.metalDisplayLink != 0 {
|
||||
v.drawableDoneCh <- struct{}{}
|
||||
return
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+246
-137
@@ -32,9 +32,13 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir"
|
||||
)
|
||||
|
||||
var sel_supportsFamily = objc.RegisterName("supportsFamily:")
|
||||
|
||||
type Graphics struct {
|
||||
view view
|
||||
|
||||
colorSpace graphicsdriver.ColorSpace
|
||||
|
||||
cq mtl.CommandQueue
|
||||
cb mtl.CommandBuffer
|
||||
rce mtl.RenderCommandEncoder
|
||||
@@ -42,7 +46,15 @@ type Graphics struct {
|
||||
|
||||
screenDrawable ca.MetalDrawable
|
||||
|
||||
buffers map[mtl.CommandBuffer][]mtl.Buffer
|
||||
// frame is the current frame number.
|
||||
// frame is incremented when the screen is presented.
|
||||
frame int64
|
||||
|
||||
// frameToCB maps a frame number to command buffers used in the frame.
|
||||
// frameToCB keeps command buffers not to be released until the command buffers are completed.
|
||||
frameToCB map[int64][]mtl.CommandBuffer
|
||||
|
||||
buffers map[int64][]mtl.Buffer
|
||||
unusedBuffers map[mtl.Buffer]struct{}
|
||||
|
||||
lastDst *Image
|
||||
@@ -90,7 +102,7 @@ func init() {
|
||||
|
||||
// NewGraphics creates an implementation of graphicsdriver.Graphics for Metal.
|
||||
// The returned graphics value is nil iff the error is not nil.
|
||||
func NewGraphics() (graphicsdriver.Graphics, error) {
|
||||
func NewGraphics(colorSpace graphicsdriver.ColorSpace) (graphicsdriver.Graphics, error) {
|
||||
// On old mac devices like iMac 2011, Metal is not supported (#779).
|
||||
// TODO: Is there a better way to check whether Metal is available or not?
|
||||
// It seems OK to call MTLCreateSystemDefaultDevice multiple times, so this should be fine.
|
||||
@@ -98,12 +110,14 @@ func NewGraphics() (graphicsdriver.Graphics, error) {
|
||||
return nil, fmt.Errorf("metal: mtl.CreateSystemDefaultDevice failed: %w", systemDefaultDeviceErr)
|
||||
}
|
||||
|
||||
g := &Graphics{}
|
||||
g := &Graphics{
|
||||
colorSpace: colorSpace,
|
||||
}
|
||||
|
||||
if runtime.GOOS != "ios" {
|
||||
// Initializing a Metal device and a layer must be done in the main thread on macOS.
|
||||
// Note that this assumes NewGraphics is called on the main thread on desktops.
|
||||
if err := g.view.initialize(systemDefaultDevice); err != nil {
|
||||
if err := g.view.initialize(systemDefaultDevice, colorSpace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -118,10 +132,12 @@ func (g *Graphics) Begin() error {
|
||||
}
|
||||
|
||||
func (g *Graphics) End(present bool) error {
|
||||
g.flushIfNeeded(present)
|
||||
g.screenDrawable = ca.MetalDrawable{}
|
||||
g.flushCommandBufferIfNeeded(present)
|
||||
g.pool.Release()
|
||||
g.pool.ID = 0
|
||||
if present {
|
||||
g.frame++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -149,21 +165,30 @@ func pow2(x uintptr) uintptr {
|
||||
}
|
||||
|
||||
func (g *Graphics) gcBuffers() {
|
||||
for cb, bs := range g.buffers {
|
||||
// If the command buffer still lives, the buffer must not be updated.
|
||||
// TODO: Handle an error?
|
||||
if cb.Status() != mtl.CommandBufferStatusCompleted {
|
||||
loop:
|
||||
for frame, bs := range g.buffers {
|
||||
if frame == g.frame {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if all command buffers for the frame are completed.
|
||||
for _, cb := range g.frameToCB[frame] {
|
||||
if cb.Status() != mtl.CommandBufferStatusCompleted {
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
for _, cb := range g.frameToCB[frame] {
|
||||
cb.Release()
|
||||
}
|
||||
delete(g.frameToCB, frame)
|
||||
|
||||
for _, b := range bs {
|
||||
if g.unusedBuffers == nil {
|
||||
g.unusedBuffers = map[mtl.Buffer]struct{}{}
|
||||
}
|
||||
g.unusedBuffers[b] = struct{}{}
|
||||
}
|
||||
delete(g.buffers, cb)
|
||||
cb.Release()
|
||||
delete(g.buffers, frame)
|
||||
}
|
||||
|
||||
const maxUnusedBuffers = 10
|
||||
@@ -182,10 +207,20 @@ func (g *Graphics) gcBuffers() {
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer {
|
||||
if g.cb == (mtl.CommandBuffer{}) {
|
||||
g.cb = g.cq.MakeCommandBuffer()
|
||||
func (g *Graphics) ensureCommandBuffer() {
|
||||
if g.cb != (mtl.CommandBuffer{}) {
|
||||
return
|
||||
}
|
||||
g.cb = g.cq.CommandBuffer()
|
||||
if g.frameToCB == nil {
|
||||
g.frameToCB = map[int64][]mtl.CommandBuffer{}
|
||||
}
|
||||
g.frameToCB[g.frame] = append(g.frameToCB[g.frame], g.cb)
|
||||
g.cb.Retain()
|
||||
}
|
||||
|
||||
func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer {
|
||||
g.ensureCommandBuffer()
|
||||
|
||||
var newBuf mtl.Buffer
|
||||
for b := range g.unusedBuffers {
|
||||
@@ -197,16 +232,13 @@ func (g *Graphics) availableBuffer(length uintptr) mtl.Buffer {
|
||||
}
|
||||
|
||||
if newBuf == (mtl.Buffer{}) {
|
||||
newBuf = g.view.getMTLDevice().MakeBufferWithLength(pow2(length), resourceStorageMode)
|
||||
newBuf = g.view.getMTLDevice().NewBufferWithLength(pow2(length), resourceStorageMode)
|
||||
}
|
||||
|
||||
if g.buffers == nil {
|
||||
g.buffers = map[mtl.CommandBuffer][]mtl.Buffer{}
|
||||
g.buffers = map[int64][]mtl.Buffer{}
|
||||
}
|
||||
if _, ok := g.buffers[g.cb]; !ok {
|
||||
g.cb.Retain()
|
||||
}
|
||||
g.buffers[g.cb] = append(g.buffers[g.cb], newBuf)
|
||||
g.buffers[g.frame] = append(g.buffers[g.frame], newBuf)
|
||||
return newBuf
|
||||
}
|
||||
|
||||
@@ -223,21 +255,21 @@ func (g *Graphics) SetVertices(vertices []float32, indices []uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Graphics) flushIfNeeded(present bool) {
|
||||
if g.cb == (mtl.CommandBuffer{}) && !present {
|
||||
func (g *Graphics) flushCommandBufferIfNeeded(present bool) {
|
||||
if g.cb == (mtl.CommandBuffer{}) {
|
||||
if g.rce != (mtl.RenderCommandEncoder{}) {
|
||||
panic("metal: render command encoder must be empty if command buffer is empty")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
g.flushRenderCommandEncoderIfNeeded()
|
||||
|
||||
if present {
|
||||
// This check is necessary when skipping to render the screen (SetScreenClearedEveryFrame(false)).
|
||||
if g.screenDrawable == (ca.MetalDrawable{}) && g.cb != (mtl.CommandBuffer{}) {
|
||||
g.screenDrawable = g.view.nextDrawable()
|
||||
}
|
||||
if g.screenDrawable != (ca.MetalDrawable{}) {
|
||||
g.cb.PresentDrawable(g.screenDrawable)
|
||||
}
|
||||
var presented bool
|
||||
if present && g.screenDrawable != (ca.MetalDrawable{}) {
|
||||
g.cb.PresentDrawable(g.screenDrawable)
|
||||
g.screenDrawable = ca.MetalDrawable{}
|
||||
presented = true
|
||||
}
|
||||
|
||||
g.cb.Commit()
|
||||
@@ -248,6 +280,10 @@ func (g *Graphics) flushIfNeeded(present bool) {
|
||||
g.tmpTextures = g.tmpTextures[:0]
|
||||
|
||||
g.cb = mtl.CommandBuffer{}
|
||||
|
||||
if presented {
|
||||
g.view.finishDrawableUsage()
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Graphics) checkSize(width, height int) {
|
||||
@@ -286,7 +322,7 @@ func (g *Graphics) NewImage(width, height int) (graphicsdriver.Image, error) {
|
||||
StorageMode: storageMode,
|
||||
Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget,
|
||||
}
|
||||
t := g.view.getMTLDevice().MakeTexture(td)
|
||||
t := g.view.getMTLDevice().NewTextureWithDescriptor(td)
|
||||
i := &Image{
|
||||
id: g.genNextImageID(),
|
||||
graphics: g,
|
||||
@@ -388,16 +424,17 @@ func (g *Graphics) Initialize() error {
|
||||
|
||||
if runtime.GOOS == "ios" {
|
||||
// Initializing a Metal device and a layer must be done in the render thread on iOS.
|
||||
if err := g.view.initialize(systemDefaultDevice); err != nil {
|
||||
if err := g.view.initialize(systemDefaultDevice, g.colorSpace); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if g.transparent {
|
||||
g.view.ml.SetOpaque(false)
|
||||
}
|
||||
// The default value is false [1], but transparinting doesn't work without calling this.
|
||||
// To avoid confusion, let's call this explicitly.
|
||||
// [1] https://developer.apple.com/documentation/quartzcore/calayer/isopaque?language=objc
|
||||
g.view.ml.SetOpaque(!g.transparent)
|
||||
|
||||
// The stencil reference value is always 0 (default).
|
||||
g.dsss[noStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
g.dsss[noStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
@@ -411,7 +448,7 @@ func (g *Graphics) Initialize() error {
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
})
|
||||
g.dsss[incrementStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
g.dsss[incrementStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
@@ -425,7 +462,7 @@ func (g *Graphics) Initialize() error {
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
})
|
||||
g.dsss[invertStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
g.dsss[invertStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
@@ -439,7 +476,7 @@ func (g *Graphics) Initialize() error {
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
})
|
||||
g.dsss[drawWithStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
g.dsss[drawWithStencil] = g.view.getMTLDevice().NewDepthStencilStateWithDescriptor(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
@@ -454,7 +491,7 @@ func (g *Graphics) Initialize() error {
|
||||
},
|
||||
})
|
||||
|
||||
g.cq = g.view.getMTLDevice().MakeCommandQueue()
|
||||
g.cq = g.view.getMTLDevice().NewCommandQueue()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -467,11 +504,18 @@ func (g *Graphics) flushRenderCommandEncoderIfNeeded() {
|
||||
g.lastDst = nil
|
||||
}
|
||||
|
||||
func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs [graphics.ShaderImageCount]*Image, indexOffset int, shader *Shader, uniforms [][]uint32, blend graphicsdriver.Blend, fillRule graphicsdriver.FillRule) error {
|
||||
func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs [graphics.ShaderSrcImageCount]*Image, indexOffset int, shader *Shader, uniforms []uint32, blend graphicsdriver.Blend, fillRule graphicsdriver.FillRule) error {
|
||||
// In order to create a separate command buffer for the screen, flush the current command buffer.
|
||||
// It's because a drawable will not be released as long as the CommandBuffer referencing it is alive,
|
||||
// it is more efficient to separate CommandBuffers that use the drawable from those that do not.
|
||||
if (g.lastDst != nil && g.lastDst.screen) != dst.screen {
|
||||
g.flushCommandBufferIfNeeded(false)
|
||||
}
|
||||
|
||||
// When preparing a stencil buffer, flush the current render command encoder
|
||||
// to make sure the stencil buffer is cleared when loading.
|
||||
// TODO: What about clearing the stencil buffer by vertices?
|
||||
if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillAll {
|
||||
if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillRuleFillAll {
|
||||
g.flushRenderCommandEncoderIfNeeded()
|
||||
}
|
||||
g.lastDst = dst
|
||||
@@ -497,17 +541,15 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
rpd.ColorAttachments[0].Texture = t
|
||||
rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{}
|
||||
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
dst.ensureStencil()
|
||||
rpd.StencilAttachment.LoadAction = mtl.LoadActionClear
|
||||
rpd.StencilAttachment.StoreAction = mtl.StoreActionDontCare
|
||||
rpd.StencilAttachment.Texture = dst.stencil
|
||||
}
|
||||
|
||||
if g.cb == (mtl.CommandBuffer{}) {
|
||||
g.cb = g.cq.MakeCommandBuffer()
|
||||
}
|
||||
g.rce = g.cb.MakeRenderCommandEncoder(rpd)
|
||||
g.ensureCommandBuffer()
|
||||
g.rce = g.cb.RenderCommandEncoderWithDescriptor(rpd)
|
||||
}
|
||||
|
||||
w, h := dst.internalSize()
|
||||
@@ -521,12 +563,11 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
})
|
||||
g.rce.SetVertexBuffer(g.vb, 0, 0)
|
||||
|
||||
for i, u := range uniforms {
|
||||
if u == nil {
|
||||
continue
|
||||
}
|
||||
g.rce.SetVertexBytes(unsafe.Pointer(&u[0]), unsafe.Sizeof(u[0])*uintptr(len(u)), i+1)
|
||||
g.rce.SetFragmentBytes(unsafe.Pointer(&u[0]), unsafe.Sizeof(u[0])*uintptr(len(u)), i+1)
|
||||
if len(uniforms) > 0 {
|
||||
uniforms := adjustUniformVariablesLayout(shader.ir.Uniforms, uniforms)
|
||||
head := unsafe.SliceData(uniforms)
|
||||
g.rce.SetVertexBytes(unsafe.Pointer(head), unsafe.Sizeof(uniforms[0])*uintptr(len(uniforms)), 1)
|
||||
g.rce.SetFragmentBytes(unsafe.Pointer(head), unsafe.Sizeof(uniforms[0])*uintptr(len(uniforms)), 0)
|
||||
}
|
||||
|
||||
for i, src := range srcs {
|
||||
@@ -544,26 +585,26 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
drawWithStencilRpss mtl.RenderPipelineState
|
||||
)
|
||||
switch fillRule {
|
||||
case graphicsdriver.FillAll:
|
||||
case graphicsdriver.FillRuleFillAll:
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, noStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
noStencilRpss = s
|
||||
case graphicsdriver.NonZero:
|
||||
case graphicsdriver.FillRuleNonZero:
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, incrementStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
incrementStencilRpss = s
|
||||
case graphicsdriver.EvenOdd:
|
||||
case graphicsdriver.FillRuleEvenOdd:
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, invertStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
invertStencilRpss = s
|
||||
}
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, drawWithStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -580,20 +621,20 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
})
|
||||
|
||||
switch fillRule {
|
||||
case graphicsdriver.FillAll:
|
||||
case graphicsdriver.FillRuleFillAll:
|
||||
g.rce.SetDepthStencilState(g.dsss[noStencil])
|
||||
g.rce.SetRenderPipelineState(noStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
case graphicsdriver.NonZero:
|
||||
case graphicsdriver.FillRuleNonZero:
|
||||
g.rce.SetDepthStencilState(g.dsss[incrementStencil])
|
||||
g.rce.SetRenderPipelineState(incrementStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
case graphicsdriver.EvenOdd:
|
||||
case graphicsdriver.FillRuleEvenOdd:
|
||||
g.rce.SetDepthStencilState(g.dsss[invertStencil])
|
||||
g.rce.SetRenderPipelineState(invertStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
}
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if fillRule != graphicsdriver.FillRuleFillAll {
|
||||
g.rce.SetDepthStencilState(g.dsss[drawWithStencil])
|
||||
g.rce.SetRenderPipelineState(drawWithStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
@@ -605,7 +646,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.ShaderSrcImageCount]graphicsdriver.ImageID, shaderID graphicsdriver.ShaderID, dstRegions []graphicsdriver.DstRegion, indexOffset int, blend graphicsdriver.Blend, uniforms []uint32, fillRule graphicsdriver.FillRule) error {
|
||||
if shaderID == graphicsdriver.InvalidShaderID {
|
||||
return fmt.Errorf("metal: shader ID is invalid")
|
||||
}
|
||||
@@ -616,72 +657,12 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
|
||||
g.view.update()
|
||||
}
|
||||
|
||||
var srcs [graphics.ShaderImageCount]*Image
|
||||
var srcs [graphics.ShaderSrcImageCount]*Image
|
||||
for i, srcID := range srcIDs {
|
||||
srcs[i] = g.images[srcID]
|
||||
}
|
||||
|
||||
uniformVars := make([][]uint32, len(g.shaders[shaderID].ir.Uniforms))
|
||||
|
||||
// Set the additional uniform variables.
|
||||
var idx int
|
||||
for i, t := range g.shaders[shaderID].ir.Uniforms {
|
||||
if i == graphics.ProjectionMatrixUniformVariableIndex {
|
||||
// In Metal, the NDC's Y direction (upward) and the framebuffer's Y direction (downward) don't
|
||||
// match. Then, the Y direction must be inverted.
|
||||
// Invert the sign bits as float32 values.
|
||||
uniforms[idx+1] ^= 1 << 31
|
||||
uniforms[idx+5] ^= 1 << 31
|
||||
uniforms[idx+9] ^= 1 << 31
|
||||
uniforms[idx+13] ^= 1 << 31
|
||||
}
|
||||
|
||||
n := t.Uint32Count()
|
||||
|
||||
switch t.Main {
|
||||
case shaderir.Vec3, shaderir.IVec3:
|
||||
// float3 requires 16-byte alignment (#2463).
|
||||
v1 := make([]uint32, 4)
|
||||
copy(v1[0:3], uniforms[idx:idx+3])
|
||||
uniformVars[i] = v1
|
||||
case shaderir.Mat3:
|
||||
// float3x3 requires 16-byte alignment (#2036).
|
||||
v1 := make([]uint32, 12)
|
||||
copy(v1[0:3], uniforms[idx:idx+3])
|
||||
copy(v1[4:7], uniforms[idx+3:idx+6])
|
||||
copy(v1[8:11], uniforms[idx+6:idx+9])
|
||||
uniformVars[i] = v1
|
||||
case shaderir.Array:
|
||||
switch t.Sub[0].Main {
|
||||
case shaderir.Vec3, shaderir.IVec3:
|
||||
v1 := make([]uint32, t.Length*4)
|
||||
for j := 0; j < t.Length; j++ {
|
||||
offset0 := j * 3
|
||||
offset1 := j * 4
|
||||
copy(v1[offset1:offset1+3], uniforms[idx+offset0:idx+offset0+3])
|
||||
}
|
||||
uniformVars[i] = v1
|
||||
case shaderir.Mat3:
|
||||
v1 := make([]uint32, t.Length*12)
|
||||
for j := 0; j < t.Length; j++ {
|
||||
offset0 := j * 9
|
||||
offset1 := j * 12
|
||||
copy(v1[offset1:offset1+3], uniforms[idx+offset0:idx+offset0+3])
|
||||
copy(v1[offset1+4:offset1+7], uniforms[idx+offset0+3:idx+offset0+6])
|
||||
copy(v1[offset1+8:offset1+11], uniforms[idx+offset0+6:idx+offset0+9])
|
||||
}
|
||||
uniformVars[i] = v1
|
||||
default:
|
||||
uniformVars[i] = uniforms[idx : idx+n]
|
||||
}
|
||||
default:
|
||||
uniformVars[i] = uniforms[idx : idx+n]
|
||||
}
|
||||
|
||||
idx += n
|
||||
}
|
||||
|
||||
if err := g.draw(dst, dstRegions, srcs, indexOffset, g.shaders[shaderID], uniformVars, blend, fillRule); err != nil {
|
||||
if err := g.draw(dst, dstRegions, srcs, indexOffset, g.shaders[shaderID], uniforms, blend, fillRule); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -705,7 +686,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 {
|
||||
@@ -802,16 +783,16 @@ func (i *Image) Dispose() {
|
||||
}
|
||||
|
||||
func (i *Image) syncTexture() {
|
||||
i.graphics.flushRenderCommandEncoderIfNeeded()
|
||||
i.graphics.flushCommandBufferIfNeeded(false)
|
||||
|
||||
// Calling SynchronizeTexture is ignored on iOS (see mtl.m), but it looks like committing BlitCommandEncoder
|
||||
// is necessary (#1337).
|
||||
if i.graphics.cb != (mtl.CommandBuffer{}) {
|
||||
panic("metal: command buffer must be empty at syncTexture: flushIfNeeded is not called yet?")
|
||||
panic("metal: command buffer must be empty at syncTexture")
|
||||
}
|
||||
|
||||
cb := i.graphics.cq.MakeCommandBuffer()
|
||||
bce := cb.MakeBlitCommandEncoder()
|
||||
cb := i.graphics.cq.CommandBuffer()
|
||||
bce := cb.BlitCommandEncoder()
|
||||
bce.SynchronizeTexture(i.texture, 0, 0)
|
||||
bce.EndEncoding()
|
||||
|
||||
@@ -821,7 +802,6 @@ func (i *Image) syncTexture() {
|
||||
}
|
||||
|
||||
func (i *Image) ReadPixels(args []graphicsdriver.PixelsArgs) error {
|
||||
i.graphics.flushIfNeeded(false)
|
||||
i.syncTexture()
|
||||
|
||||
for _, arg := range args {
|
||||
@@ -859,7 +839,7 @@ func (i *Image) WritePixels(args []graphicsdriver.PixelsArgs) error {
|
||||
StorageMode: storageMode,
|
||||
Usage: mtl.TextureUsageShaderRead | mtl.TextureUsageRenderTarget,
|
||||
}
|
||||
t := g.view.getMTLDevice().MakeTexture(td)
|
||||
t := g.view.getMTLDevice().NewTextureWithDescriptor(td)
|
||||
g.tmpTextures = append(g.tmpTextures, t)
|
||||
|
||||
for _, a := range args {
|
||||
@@ -869,10 +849,8 @@ func (i *Image) WritePixels(args []graphicsdriver.PixelsArgs) error {
|
||||
}, 0, unsafe.Pointer(&a.Pixels[0]), 4*a.Region.Dx())
|
||||
}
|
||||
|
||||
if g.cb == (mtl.CommandBuffer{}) {
|
||||
g.cb = i.graphics.cq.MakeCommandBuffer()
|
||||
}
|
||||
bce := g.cb.MakeBlitCommandEncoder()
|
||||
g.ensureCommandBuffer()
|
||||
bce := g.cb.BlitCommandEncoder()
|
||||
for _, a := range args {
|
||||
so := mtl.Origin{X: a.Region.Min.X - region.Min.X, Y: a.Region.Min.Y - region.Min.Y, Z: 0}
|
||||
ss := mtl.Size{Width: a.Region.Dx(), Height: a.Region.Dy(), Depth: 1}
|
||||
@@ -896,6 +874,9 @@ func (i *Image) mtlTexture() mtl.Texture {
|
||||
// After nextDrawable, it is expected some command buffers are completed.
|
||||
g.gcBuffers()
|
||||
}
|
||||
if g.screenDrawable == (ca.MetalDrawable{}) {
|
||||
return mtl.Texture{}
|
||||
}
|
||||
return g.screenDrawable.Texture()
|
||||
}
|
||||
return i.texture
|
||||
@@ -914,5 +895,133 @@ func (i *Image) ensureStencil() {
|
||||
StorageMode: mtl.StorageModePrivate,
|
||||
Usage: mtl.TextureUsageRenderTarget,
|
||||
}
|
||||
i.stencil = i.graphics.view.getMTLDevice().MakeTexture(td)
|
||||
i.stencil = i.graphics.view.getMTLDevice().NewTextureWithDescriptor(td)
|
||||
}
|
||||
|
||||
// adjustUniformVariablesLayout returns adjusted uniform variables to match the Metal's memory layout.
|
||||
func adjustUniformVariablesLayout(uniformTypes []shaderir.Type, uniforms []uint32) []uint32 {
|
||||
// Each type's alignment is defined by the specification.
|
||||
// See https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf
|
||||
|
||||
var values []uint32
|
||||
fillZerosToFitAlignment := func(values []uint32, align int) []uint32 {
|
||||
if len(values) == 0 {
|
||||
return values
|
||||
}
|
||||
n0 := len(values)
|
||||
n1 := ((len(values)-1)/align + 1) * align
|
||||
if n0 == n1 {
|
||||
return values
|
||||
}
|
||||
return append(values, make([]uint32, n1-n0)...)
|
||||
}
|
||||
|
||||
var idx int
|
||||
var byteAlign int
|
||||
for i, typ := range uniformTypes {
|
||||
n := typ.DwordCount()
|
||||
switch typ.Main {
|
||||
case shaderir.Bool:
|
||||
if byteAlign == 0 {
|
||||
values = append(values, uniforms[idx:idx+1]...)
|
||||
} else {
|
||||
values[len(values)-1] |= uniforms[idx] << (8 * byteAlign)
|
||||
}
|
||||
case shaderir.Float, shaderir.Int:
|
||||
values = append(values, uniforms[idx:idx+n]...)
|
||||
case shaderir.Vec2, shaderir.IVec2:
|
||||
values = fillZerosToFitAlignment(values, 2)
|
||||
values = append(values, uniforms[idx:idx+n]...)
|
||||
case shaderir.Vec3, shaderir.IVec3:
|
||||
values = fillZerosToFitAlignment(values, 4)
|
||||
values = append(values, uniforms[idx:idx+n]...)
|
||||
values = append(values, 0)
|
||||
case shaderir.Vec4, shaderir.IVec4:
|
||||
values = fillZerosToFitAlignment(values, 4)
|
||||
values = append(values, uniforms[idx:idx+n]...)
|
||||
case shaderir.Mat2:
|
||||
values = fillZerosToFitAlignment(values, 2)
|
||||
values = append(values, uniforms[idx:idx+n]...)
|
||||
case shaderir.Mat3:
|
||||
values = fillZerosToFitAlignment(values, 4)
|
||||
values = append(values, uniforms[idx:idx+3]...)
|
||||
values = append(values, 0)
|
||||
values = append(values, uniforms[idx+3:idx+6]...)
|
||||
values = append(values, 0)
|
||||
values = append(values, uniforms[idx+6:idx+9]...)
|
||||
values = append(values, 0)
|
||||
case shaderir.Mat4:
|
||||
values = fillZerosToFitAlignment(values, 4)
|
||||
if i == graphics.ProjectionMatrixUniformVariableIndex {
|
||||
// In Metal, the NDC's Y direction (upward) and the framebuffer's Y direction (downward) don't
|
||||
// match. Then, the Y direction must be inverted.
|
||||
// Invert the sign bits as float32 values.
|
||||
u := uniforms[idx : idx+16]
|
||||
values = append(values,
|
||||
u[0], u[1]^uint32(1<<31), u[2], u[3],
|
||||
u[4], u[5]^uint32(1<<31), u[6], u[7],
|
||||
u[8], u[9]^uint32(1<<31), u[10], u[11],
|
||||
u[12], u[13]^uint32(1<<31), u[14], u[15],
|
||||
)
|
||||
} else {
|
||||
values = append(values, uniforms[idx:idx+n]...)
|
||||
}
|
||||
case shaderir.Array:
|
||||
switch typ.Sub[0].Main {
|
||||
case shaderir.Bool:
|
||||
for i := range n {
|
||||
if (i+byteAlign)%4 == 0 {
|
||||
values = append(values, uniforms[idx+i])
|
||||
} else {
|
||||
values[len(values)-1] |= uniforms[idx+i] << (8 * ((i + byteAlign) % 4))
|
||||
}
|
||||
}
|
||||
case shaderir.Float, shaderir.Int:
|
||||
values = append(values, uniforms[idx:idx+n]...)
|
||||
case shaderir.Vec2, shaderir.IVec2:
|
||||
values = fillZerosToFitAlignment(values, 2)
|
||||
values = append(values, uniforms[idx:idx+n]...)
|
||||
case shaderir.Vec3, shaderir.IVec3:
|
||||
values = fillZerosToFitAlignment(values, 4)
|
||||
for j := 0; j < typ.Length; j++ {
|
||||
values = append(values, uniforms[idx+3*j:idx+3*(j+1)]...)
|
||||
values = append(values, 0)
|
||||
}
|
||||
case shaderir.Vec4, shaderir.IVec4:
|
||||
values = fillZerosToFitAlignment(values, 4)
|
||||
values = append(values, uniforms[idx:idx+n]...)
|
||||
case shaderir.Mat2:
|
||||
values = fillZerosToFitAlignment(values, 2)
|
||||
values = append(values, uniforms[idx:idx+n]...)
|
||||
case shaderir.Mat3:
|
||||
values = fillZerosToFitAlignment(values, 4)
|
||||
for j := 0; j < typ.Length; j++ {
|
||||
values = append(values, uniforms[idx+9*j:idx+9*j+3]...)
|
||||
values = append(values, 0)
|
||||
values = append(values, uniforms[idx+9*j+3:idx+9*j+6]...)
|
||||
values = append(values, 0)
|
||||
values = append(values, uniforms[idx+9*j+6:idx+9*j+9]...)
|
||||
values = append(values, 0)
|
||||
}
|
||||
case shaderir.Mat4:
|
||||
values = fillZerosToFitAlignment(values, 4)
|
||||
values = append(values, uniforms[idx:idx+n]...)
|
||||
default:
|
||||
panic(fmt.Sprintf("metal: not implemented type for uniform variables: %s", typ.String()))
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("metal: not implemented type for uniform variables: %s", typ.String()))
|
||||
}
|
||||
|
||||
idx += n
|
||||
|
||||
if typ.Main == shaderir.Bool || (typ.Main == shaderir.Array && typ.Sub[0].Main == shaderir.Bool) {
|
||||
byteAlign += n
|
||||
byteAlign %= 4
|
||||
} else {
|
||||
byteAlign = 0
|
||||
}
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
Generated
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
// Copyright 2024 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 mtl
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
var libSystem uintptr
|
||||
|
||||
var (
|
||||
dispatchDataCreate func(buffer unsafe.Pointer, size uint, queue uintptr, destructor uintptr) uintptr
|
||||
dispatchRelease func(obj uintptr)
|
||||
)
|
||||
|
||||
func init() {
|
||||
lib, err := purego.Dlopen("/usr/lib/libSystem.B.dylib", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
libSystem = lib
|
||||
|
||||
purego.RegisterLibFunc(&dispatchDataCreate, libSystem, "dispatch_data_create")
|
||||
purego.RegisterLibFunc(&dispatchRelease, libSystem, "dispatch_release")
|
||||
}
|
||||
Generated
Vendored
+160
-144
@@ -36,7 +36,7 @@ import (
|
||||
|
||||
// GPUFamily represents the functionality for families of GPUs.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlgpufamily
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlgpufamily?language=objc.
|
||||
type GPUFamily int
|
||||
|
||||
const (
|
||||
@@ -54,7 +54,7 @@ const (
|
||||
|
||||
// FeatureSet defines a specific platform, hardware, and software configuration.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlfeatureset.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlfeatureset?language=objc.
|
||||
type FeatureSet uint16
|
||||
|
||||
const (
|
||||
@@ -92,7 +92,7 @@ const (
|
||||
// TextureType defines The dimension of each image, including whether multiple images are arranged into an array or
|
||||
// a cube.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexturetype
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexturetype?language=objc.
|
||||
type TextureType uint16
|
||||
|
||||
const (
|
||||
@@ -102,7 +102,7 @@ const (
|
||||
// PixelFormat defines data formats that describe the organization
|
||||
// and characteristics of individual pixels in a texture.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlpixelformat.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlpixelformat?language=objc.
|
||||
type PixelFormat uint16
|
||||
|
||||
// The data formats that describe the organization and characteristics
|
||||
@@ -117,7 +117,7 @@ const (
|
||||
|
||||
// PrimitiveType defines geometric primitive types for drawing commands.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlprimitivetype.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlprimitivetype?language=objc.
|
||||
type PrimitiveType uint8
|
||||
|
||||
// Geometric primitive types for drawing commands.
|
||||
@@ -132,7 +132,7 @@ const (
|
||||
// LoadAction defines actions performed at the start of a rendering pass
|
||||
// for a render command encoder.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlloadaction.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlloadaction?language=objc.
|
||||
type LoadAction uint8
|
||||
|
||||
// Actions performed at the start of a rendering pass for a render command encoder.
|
||||
@@ -145,7 +145,7 @@ const (
|
||||
// StoreAction defines actions performed at the end of a rendering pass
|
||||
// for a render command encoder.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoreaction.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoreaction?language=objc.
|
||||
type StoreAction uint8
|
||||
|
||||
// Actions performed at the end of a rendering pass for a render command encoder.
|
||||
@@ -160,7 +160,7 @@ const (
|
||||
|
||||
// StorageMode defines the memory location and access permissions of a resource.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode?language=objc.
|
||||
type StorageMode uint8
|
||||
|
||||
const (
|
||||
@@ -189,7 +189,7 @@ const (
|
||||
// ResourceOptions defines optional arguments used to create
|
||||
// and influence behavior of buffer and texture objects.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlresourceoptions.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlresourceoptions?language=objc.
|
||||
type ResourceOptions uint16
|
||||
|
||||
const (
|
||||
@@ -237,7 +237,7 @@ const (
|
||||
|
||||
// CPUCacheMode is the CPU cache mode that defines the CPU mapping of a resource.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcpucachemode.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcpucachemode?language=objc.
|
||||
type CPUCacheMode uint8
|
||||
|
||||
const (
|
||||
@@ -252,7 +252,7 @@ const (
|
||||
|
||||
// IndexType is the index type for an index buffer that references vertices of geometric primitives.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstoragemode?language=objc
|
||||
type IndexType uint8
|
||||
|
||||
const (
|
||||
@@ -358,7 +358,7 @@ const (
|
||||
// Resource represents a memory allocation for storing specialized data
|
||||
// that is accessible to the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlresource.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlresource?language=objc.
|
||||
type Resource interface {
|
||||
// resource returns the underlying id<MTLResource> pointer.
|
||||
resource() unsafe.Pointer
|
||||
@@ -366,7 +366,7 @@ type Resource interface {
|
||||
|
||||
// RenderPipelineDescriptor configures new RenderPipelineState objects.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinedescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinedescriptor?language=objc.
|
||||
type RenderPipelineDescriptor struct {
|
||||
// VertexFunction is a programmable function that processes individual vertices in a rendering pass.
|
||||
VertexFunction Function
|
||||
@@ -384,7 +384,7 @@ type RenderPipelineDescriptor struct {
|
||||
// RenderPipelineColorAttachmentDescriptor describes a color render target that specifies
|
||||
// the color configuration and color operations associated with a render pipeline.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinecolorattachmentdescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinecolorattachmentdescriptor?language=objc.
|
||||
type RenderPipelineColorAttachmentDescriptor struct {
|
||||
// PixelFormat is the pixel format of the color attachment's texture.
|
||||
PixelFormat PixelFormat
|
||||
@@ -404,7 +404,7 @@ type RenderPipelineColorAttachmentDescriptor struct {
|
||||
// RenderPassDescriptor describes a group of render targets that serve as
|
||||
// the output destination for pixels generated by a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassdescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassdescriptor?language=objc.
|
||||
type RenderPassDescriptor struct {
|
||||
// ColorAttachments is array of state information for attachments that store color data.
|
||||
ColorAttachments [1]RenderPassColorAttachmentDescriptor
|
||||
@@ -416,7 +416,7 @@ type RenderPassDescriptor struct {
|
||||
// RenderPassColorAttachmentDescriptor describes a color render target that serves
|
||||
// as the output destination for color pixels generated by a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpasscolorattachmentdescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpasscolorattachmentdescriptor?language=objc.
|
||||
type RenderPassColorAttachmentDescriptor struct {
|
||||
RenderPassAttachmentDescriptor
|
||||
ClearColor ClearColor
|
||||
@@ -425,7 +425,7 @@ type RenderPassColorAttachmentDescriptor struct {
|
||||
// RenderPassStencilAttachment describes a stencil render target that serves as the output
|
||||
// destination for stencil pixels generated by a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassstencilattachmentdescriptor
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassstencilattachmentdescriptor?language=objc.
|
||||
type RenderPassStencilAttachment struct {
|
||||
RenderPassAttachmentDescriptor
|
||||
}
|
||||
@@ -433,7 +433,7 @@ type RenderPassStencilAttachment struct {
|
||||
// RenderPassAttachmentDescriptor describes a render target that serves
|
||||
// as the output destination for pixels generated by a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassattachmentdescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpassattachmentdescriptor?language=objc.
|
||||
type RenderPassAttachmentDescriptor struct {
|
||||
LoadAction LoadAction
|
||||
StoreAction StoreAction
|
||||
@@ -442,14 +442,14 @@ type RenderPassAttachmentDescriptor struct {
|
||||
|
||||
// ClearColor is an RGBA value used for a color pixel.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlclearcolor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlclearcolor?language=objc.
|
||||
type ClearColor struct {
|
||||
Red, Green, Blue, Alpha float64
|
||||
}
|
||||
|
||||
// TextureDescriptor configures new Texture objects.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexturedescriptor.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexturedescriptor?language=objc.
|
||||
type TextureDescriptor struct {
|
||||
TextureType TextureType
|
||||
PixelFormat PixelFormat
|
||||
@@ -462,7 +462,7 @@ type TextureDescriptor struct {
|
||||
// Device is abstract representation of the GPU that
|
||||
// serves as the primary interface for a Metal app.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice?language=objc.
|
||||
type Device struct {
|
||||
device objc.ID
|
||||
|
||||
@@ -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")
|
||||
@@ -493,6 +492,7 @@ var (
|
||||
sel_supportsFeatureSet = objc.RegisterName("supportsFeatureSet:")
|
||||
sel_newCommandQueue = objc.RegisterName("newCommandQueue")
|
||||
sel_newLibraryWithSource_options_error = objc.RegisterName("newLibraryWithSource:options:error:")
|
||||
sel_newLibraryWithData_error = objc.RegisterName("newLibraryWithData:error:")
|
||||
sel_release = objc.RegisterName("release")
|
||||
sel_retain = objc.RegisterName("retain")
|
||||
sel_new = objc.RegisterName("new")
|
||||
@@ -567,7 +567,7 @@ var (
|
||||
|
||||
// CreateSystemDefaultDevice returns the preferred system default Metal device.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/1433401-mtlcreatesystemdefaultdevice.
|
||||
// Reference: https://developer.apple.com/documentation/metal/1433401-mtlcreatesystemdefaultdevice?language=objc.
|
||||
func CreateSystemDefaultDevice() (Device, error) {
|
||||
metal, err := purego.Dlopen("/System/Library/Frameworks/Metal.framework/Metal", purego.RTLD_LAZY|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
@@ -607,37 +607,36 @@ func (d Device) Device() unsafe.Pointer { return *(*unsafe.Pointer)(unsafe.Point
|
||||
|
||||
// RespondsToSelector returns a Boolean value that indicates whether the receiver implements or inherits a method that can respond to a specified message.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418583-respondstoselector
|
||||
// Reference: https://developer.apple.com/documentation/objectivec/1418956-nsobject/1418583-respondstoselector?language=objc.
|
||||
func (d Device) RespondsToSelector(sel objc.SEL) bool {
|
||||
return d.device.Send(sel_respondsToSelector, sel) != 0
|
||||
}
|
||||
|
||||
// SupportsFamily returns a Boolean value that indicates whether the GPU device supports the feature set of a specific GPU family.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/3143473-supportsfamily
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/3143473-supportsfamily?language=objc.
|
||||
func (d Device) SupportsFamily(gpuFamily GPUFamily) bool {
|
||||
return d.device.Send(sel_supportsFamily, uintptr(gpuFamily)) != 0
|
||||
}
|
||||
|
||||
// SupportsFeatureSet reports whether device d supports feature set fs.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433418-supportsfeatureset.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433418-supportsfeatureset?language=objc.
|
||||
func (d Device) SupportsFeatureSet(fs FeatureSet) bool {
|
||||
return d.device.Send(sel_supportsFeatureSet, uintptr(fs)) != 0
|
||||
}
|
||||
|
||||
// MakeCommandQueue creates a serial command submission queue.
|
||||
// NewCommandQueue creates a queue you use to submit rendering and computation commands to a GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433388-makecommandqueue.
|
||||
func (d Device) MakeCommandQueue() CommandQueue {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433388-newcommandqueue?language=objc.
|
||||
func (d Device) NewCommandQueue() CommandQueue {
|
||||
return CommandQueue{d.device.Send(sel_newCommandQueue)}
|
||||
}
|
||||
|
||||
// MakeLibrary creates a new library that contains
|
||||
// the functions stored in the specified source string.
|
||||
// NewLibraryWithSource synchronously creates a Metal library instance by compiling the functions in a source string.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433431-makelibrary.
|
||||
func (d Device) MakeLibrary(source string, opt CompileOptions) (Library, error) {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433431-newlibrarywithsource?language=objc.
|
||||
func (d Device) NewLibraryWithSource(source string, opt CompileOptions) (Library, error) {
|
||||
var err cocoa.NSError
|
||||
l := d.device.Send(
|
||||
sel_newLibraryWithSource_options_error,
|
||||
@@ -652,10 +651,31 @@ func (d Device) MakeLibrary(source string, opt CompileOptions) (Library, error)
|
||||
return Library{l}, nil
|
||||
}
|
||||
|
||||
// MakeRenderPipelineState creates a render pipeline state object.
|
||||
// NewLibraryWithData Creates a Metal library instance that contains the functions in a precompiled Metal library.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433369-makerenderpipelinestate.
|
||||
func (d Device) MakeRenderPipelineState(rpd RenderPipelineDescriptor) (RenderPipelineState, error) {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433391-newlibrarywithdata?language=objc.
|
||||
func (d Device) NewLibraryWithData(buffer []byte) (Library, error) {
|
||||
defer runtime.KeepAlive(buffer)
|
||||
|
||||
data := dispatchDataCreate(unsafe.Pointer(&buffer[0]), uint(len(buffer)), 0, 0)
|
||||
defer dispatchRelease(data)
|
||||
|
||||
var err cocoa.NSError
|
||||
l := d.device.Send(
|
||||
sel_newLibraryWithData_error,
|
||||
data,
|
||||
unsafe.Pointer(&err),
|
||||
)
|
||||
if l == 0 {
|
||||
return Library{}, errors.New(cocoa.NSString{ID: err.Send(sel_localizedDescription)}.String())
|
||||
}
|
||||
return Library{l}, nil
|
||||
}
|
||||
|
||||
// NewRenderPipelineStateWithDescriptor synchronously creates a render pipeline state.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433369-newrenderpipelinestatewithdescri?language=objc.
|
||||
func (d Device) NewRenderPipelineStateWithDescriptor(rpd RenderPipelineDescriptor) (RenderPipelineState, error) {
|
||||
renderPipelineDescriptor := objc.ID(class_MTLRenderPipelineDescriptor).Send(sel_new)
|
||||
renderPipelineDescriptor.Send(sel_setVertexFunction, rpd.VertexFunction.function)
|
||||
renderPipelineDescriptor.Send(sel_setFragmentFunction, rpd.FragmentFunction.function)
|
||||
@@ -683,26 +703,24 @@ func (d Device) MakeRenderPipelineState(rpd RenderPipelineDescriptor) (RenderPip
|
||||
return RenderPipelineState{renderPipelineState}, nil
|
||||
}
|
||||
|
||||
// MakeBufferWithBytes allocates a new buffer of a given length
|
||||
// and initializes its contents by copying existing data into it.
|
||||
// NewBufferWithBytes allocates a new buffer of a given length and initializes its contents by copying existing data into it.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433429-makebuffer.
|
||||
func (d Device) MakeBufferWithBytes(bytes unsafe.Pointer, length uintptr, opt ResourceOptions) Buffer {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433429-newbufferwithbytes?language=objc.
|
||||
func (d Device) NewBufferWithBytes(bytes unsafe.Pointer, length uintptr, opt ResourceOptions) Buffer {
|
||||
return Buffer{d.device.Send(sel_newBufferWithBytes_length_options, bytes, length, uintptr(opt))}
|
||||
}
|
||||
|
||||
// MakeBufferWithLength allocates a new zero-filled buffer of a given length.
|
||||
// NewBufferWithLength allocates a new zero-filled buffer of a given length.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433375-newbufferwithlength
|
||||
func (d Device) MakeBufferWithLength(length uintptr, opt ResourceOptions) Buffer {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433375-newbufferwithlength?language=objc.
|
||||
func (d Device) NewBufferWithLength(length uintptr, opt ResourceOptions) Buffer {
|
||||
return Buffer{d.device.Send(sel_newBufferWithLength_options, length, uintptr(opt))}
|
||||
}
|
||||
|
||||
// MakeTexture creates a texture object with privately owned storage
|
||||
// that contains texture state.
|
||||
// NewTextureWithDescriptor creates a new texture instance.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433425-maketexture.
|
||||
func (d Device) MakeTexture(td TextureDescriptor) Texture {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433425-newtexturewithdescriptor?language=objc.
|
||||
func (d Device) NewTextureWithDescriptor(td TextureDescriptor) Texture {
|
||||
textureDescriptor := objc.ID(class_MTLTextureDescriptor).Send(sel_new)
|
||||
textureDescriptor.Send(sel_setTextureType, uintptr(td.TextureType))
|
||||
textureDescriptor.Send(sel_setPixelFormat, uintptr(td.PixelFormat))
|
||||
@@ -717,10 +735,10 @@ func (d Device) MakeTexture(td TextureDescriptor) Texture {
|
||||
}
|
||||
}
|
||||
|
||||
// MakeDepthStencilState creates a new object that contains depth and stencil test state.
|
||||
// NewDepthStencilStateWithDescriptor creates a depth-stencil state instance.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433412-makedepthstencilstate
|
||||
func (d Device) MakeDepthStencilState(dsd DepthStencilDescriptor) DepthStencilState {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldevice/1433412-newdepthstencilstatewithdescript?language=objc.
|
||||
func (d Device) NewDepthStencilStateWithDescriptor(dsd DepthStencilDescriptor) DepthStencilState {
|
||||
depthStencilDescriptor := objc.ID(class_MTLDepthStencilDescriptor).Send(sel_new)
|
||||
backFaceStencil := depthStencilDescriptor.Send(sel_backFaceStencil)
|
||||
backFaceStencil.Send(sel_setStencilFailureOperation, uintptr(dsd.BackFaceStencil.StencilFailureOperation))
|
||||
@@ -742,14 +760,14 @@ func (d Device) MakeDepthStencilState(dsd DepthStencilDescriptor) DepthStencilSt
|
||||
// CompileOptions specifies optional compilation settings for
|
||||
// the graphics or compute functions within a library.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcompileoptions.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcompileoptions?language=objc.
|
||||
type CompileOptions struct {
|
||||
// TODO.
|
||||
}
|
||||
|
||||
// Drawable is a displayable resource that can be rendered or written to.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldrawable.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldrawable?language=objc.
|
||||
type Drawable interface {
|
||||
// Drawable returns the underlying id<MTLDrawable> pointer.
|
||||
Drawable() unsafe.Pointer
|
||||
@@ -758,7 +776,7 @@ type Drawable interface {
|
||||
// CommandQueue is a queue that organizes the order
|
||||
// in which command buffers are executed by the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue?language=objc.
|
||||
type CommandQueue struct {
|
||||
commandQueue objc.ID
|
||||
}
|
||||
@@ -767,17 +785,17 @@ func (cq CommandQueue) Release() {
|
||||
cq.commandQueue.Send(sel_release)
|
||||
}
|
||||
|
||||
// MakeCommandBuffer creates a command buffer.
|
||||
// CommandBuffer returns a command buffer from the command queue that maintains strong references to resources.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue/1508686-makecommandbuffer.
|
||||
func (cq CommandQueue) MakeCommandBuffer() CommandBuffer {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandqueue/1508686-commandbuffer?language=objc.
|
||||
func (cq CommandQueue) CommandBuffer() CommandBuffer {
|
||||
return CommandBuffer{cq.commandQueue.Send(sel_commandBuffer)}
|
||||
}
|
||||
|
||||
// CommandBuffer is a container that stores encoded commands
|
||||
// that are committed to and executed by the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer?language=objc.
|
||||
type CommandBuffer struct {
|
||||
commandBuffer objc.ID
|
||||
}
|
||||
@@ -792,55 +810,49 @@ func (cb CommandBuffer) Release() {
|
||||
|
||||
// Status returns the current stage in the lifetime of the command buffer.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443048-status
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443048-status?language=objc.
|
||||
func (cb CommandBuffer) Status() CommandBufferStatus {
|
||||
return CommandBufferStatus(cb.commandBuffer.Send(sel_status))
|
||||
}
|
||||
|
||||
// PresentDrawable registers a drawable presentation to occur as soon as possible.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443029-presentdrawable.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443029-presentdrawable?language=objc.
|
||||
func (cb CommandBuffer) PresentDrawable(d Drawable) {
|
||||
cb.commandBuffer.Send(sel_presentDrawable, d.Drawable())
|
||||
}
|
||||
|
||||
// Commit commits this command buffer for execution as soon as possible.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443003-commit.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443003-commit?language=objc.
|
||||
func (cb CommandBuffer) Commit() {
|
||||
cb.commandBuffer.Send(sel_commit)
|
||||
}
|
||||
|
||||
// WaitUntilCompleted waits for the execution of this command buffer to complete.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443039-waituntilcompleted.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443039-waituntilcompleted?language=objc.
|
||||
func (cb CommandBuffer) WaitUntilCompleted() {
|
||||
cb.commandBuffer.Send(sel_waitUntilCompleted)
|
||||
}
|
||||
|
||||
// WaitUntilScheduled blocks execution of the current thread until the command buffer is scheduled.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443036-waituntilscheduled.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443036-waituntilscheduled?language=objc.
|
||||
func (cb CommandBuffer) WaitUntilScheduled() {
|
||||
cb.commandBuffer.Send(sel_waitUntilScheduled)
|
||||
}
|
||||
|
||||
// MakeRenderCommandEncoder creates an encoder object that can
|
||||
// encode graphics rendering commands into this command buffer.
|
||||
// RenderCommandEncoderWithDescriptor creates a render command encoder from a descriptor.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1442999-makerendercommandencoder.
|
||||
func (cb CommandBuffer) MakeRenderCommandEncoder(rpd RenderPassDescriptor) RenderCommandEncoder {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1442999-rendercommandencoderwithdescript?language=objc.
|
||||
func (cb CommandBuffer) RenderCommandEncoderWithDescriptor(rpd RenderPassDescriptor) RenderCommandEncoder {
|
||||
var renderPassDescriptor = objc.ID(class_MTLRenderPassDescriptor).Send(sel_new)
|
||||
var colorAttachments0 = renderPassDescriptor.Send(sel_colorAttachments).Send(sel_objectAtIndexedSubscript, 0)
|
||||
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))
|
||||
@@ -850,11 +862,11 @@ func (cb CommandBuffer) MakeRenderCommandEncoder(rpd RenderPassDescriptor) Rende
|
||||
return RenderCommandEncoder{CommandEncoder{rce}}
|
||||
}
|
||||
|
||||
// MakeBlitCommandEncoder creates an encoder object that can encode
|
||||
// BlitCommandEncoder creates an encoder object that can encode
|
||||
// memory operation (blit) commands into this command buffer.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443001-makeblitcommandencoder.
|
||||
func (cb CommandBuffer) MakeBlitCommandEncoder() BlitCommandEncoder {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443001-makeblitcommandencoder?language=objc.
|
||||
func (cb CommandBuffer) BlitCommandEncoder() BlitCommandEncoder {
|
||||
ce := cb.commandBuffer.Send(sel_blitCommandEncoder)
|
||||
return BlitCommandEncoder{CommandEncoder{ce}}
|
||||
}
|
||||
@@ -862,14 +874,14 @@ func (cb CommandBuffer) MakeBlitCommandEncoder() BlitCommandEncoder {
|
||||
// CommandEncoder is an encoder that writes sequential GPU commands
|
||||
// into a command buffer.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandencoder.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandbuffer/1443001-blitcommandencoder?language=objc.
|
||||
type CommandEncoder struct {
|
||||
commandEncoder objc.ID
|
||||
}
|
||||
|
||||
// EndEncoding declares that all command generation from this encoder is completed.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandencoder/1458038-endencoding.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlcommandencoder/1458038-endencoding?language=objc.
|
||||
func (ce CommandEncoder) EndEncoding() {
|
||||
ce.commandEncoder.Send(sel_endEncoding)
|
||||
}
|
||||
@@ -877,7 +889,7 @@ func (ce CommandEncoder) EndEncoding() {
|
||||
// RenderCommandEncoder is an encoder that specifies graphics-rendering commands
|
||||
// and executes graphics functions.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder?language=objc.
|
||||
type RenderCommandEncoder struct {
|
||||
CommandEncoder
|
||||
}
|
||||
@@ -888,41 +900,33 @@ func (rce RenderCommandEncoder) Release() {
|
||||
|
||||
// SetRenderPipelineState sets the current render pipeline state object.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515811-setrenderpipelinestate.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515811-setrenderpipelinestate?language=objc.
|
||||
func (rce RenderCommandEncoder) SetRenderPipelineState(rps RenderPipelineState) {
|
||||
rce.commandEncoder.Send(sel_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.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515583-setscissorrect
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515583-setscissorrect?language=objc.
|
||||
func (rce RenderCommandEncoder) SetScissorRect(scissorRect ScissorRect) {
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:{MTLScissorRect=qqqq}"))
|
||||
inv.SetTarget(rce.commandEncoder)
|
||||
inv.SetSelector(sel_setScissorRect)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(&scissorRect), 2)
|
||||
inv.Invoke()
|
||||
rce.commandEncoder.Send(sel_setScissorRect, scissorRect)
|
||||
}
|
||||
|
||||
// SetVertexBuffer sets a buffer for the vertex shader function at an index
|
||||
// in the buffer argument table with an offset that specifies the start of the data.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515829-setvertexbuffer.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515829-setvertexbuffer?language=objc.
|
||||
func (rce RenderCommandEncoder) SetVertexBuffer(buf Buffer, offset, index int) {
|
||||
rce.commandEncoder.Send(sel_setVertexBuffer_offset_atIndex, buf.buffer, offset, index)
|
||||
}
|
||||
|
||||
// SetVertexBytes sets a block of data for the vertex function.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515846-setvertexbytes.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515846-setvertexbytes?language=objc.
|
||||
func (rce RenderCommandEncoder) SetVertexBytes(bytes unsafe.Pointer, length uintptr, index int) {
|
||||
rce.commandEncoder.Send(sel_setVertexBytes_length_atIndex, bytes, length, index)
|
||||
}
|
||||
@@ -933,7 +937,7 @@ func (rce RenderCommandEncoder) SetFragmentBytes(bytes unsafe.Pointer, length ui
|
||||
|
||||
// SetFragmentTexture sets a texture for the fragment function at an index in the texture argument table.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515390-setfragmenttexture
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515390-setfragmenttexture?language=objc.
|
||||
func (rce RenderCommandEncoder) SetFragmentTexture(texture Texture, index int) {
|
||||
rce.commandEncoder.Send(sel_setFragmentTexture_atIndex, texture.texture, index)
|
||||
}
|
||||
@@ -944,7 +948,7 @@ func (rce RenderCommandEncoder) SetBlendColor(red, green, blue, alpha float32) {
|
||||
|
||||
// SetDepthStencilState sets the depth and stencil test state.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516119-setdepthstencilstate
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516119-setdepthstencilstate?language=objc.
|
||||
func (rce RenderCommandEncoder) SetDepthStencilState(depthStencilState DepthStencilState) {
|
||||
rce.commandEncoder.Send(sel_setDepthStencilState, depthStencilState.depthStencilState)
|
||||
}
|
||||
@@ -952,7 +956,7 @@ func (rce RenderCommandEncoder) SetDepthStencilState(depthStencilState DepthSten
|
||||
// DrawPrimitives renders one instance of primitives using vertex data
|
||||
// in contiguous array elements.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516326-drawprimitives.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1516326-drawprimitives?language=objc.
|
||||
func (rce RenderCommandEncoder) DrawPrimitives(typ PrimitiveType, vertexStart, vertexCount int) {
|
||||
rce.commandEncoder.Send(sel_drawPrimitives_vertexStart_vertexCount, uintptr(typ), vertexStart, vertexCount)
|
||||
}
|
||||
@@ -969,7 +973,7 @@ func (rce RenderCommandEncoder) DrawIndexedPrimitives(typ PrimitiveType, indexCo
|
||||
// BlitCommandEncoder is an encoder that specifies resource copy
|
||||
// and resource synchronization commands.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder?language=objc.
|
||||
type BlitCommandEncoder struct {
|
||||
CommandEncoder
|
||||
}
|
||||
@@ -977,7 +981,7 @@ type BlitCommandEncoder struct {
|
||||
// Synchronize flushes any copy of the specified resource from its corresponding
|
||||
// Device caches and, if needed, invalidates any CPU caches.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400775-synchronize.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400775-synchronize?language=objc.
|
||||
func (bce BlitCommandEncoder) Synchronize(resource Resource) {
|
||||
if runtime.GOOS == "ios" {
|
||||
return
|
||||
@@ -985,6 +989,9 @@ func (bce BlitCommandEncoder) Synchronize(resource Resource) {
|
||||
bce.commandEncoder.Send(sel_synchronizeResource, resource.resource())
|
||||
}
|
||||
|
||||
// SynchronizeTexture encodes a command that synchronizes a part of the CPU’s copy of a texture so that it matches the GPU’s copy.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400757-synchronizetexture?language=objc.
|
||||
func (bce BlitCommandEncoder) SynchronizeTexture(texture Texture, slice int, level int) {
|
||||
if runtime.GOOS == "ios" {
|
||||
return
|
||||
@@ -992,7 +999,11 @@ func (bce BlitCommandEncoder) SynchronizeTexture(texture Texture, slice int, lev
|
||||
bce.commandEncoder.Send(sel_synchronizeTexture_slice_level, texture.texture, slice, level)
|
||||
}
|
||||
|
||||
// CopyFromTexture encodes a command that copies image data from a texture’s slice into another slice.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlblitcommandencoder/1400754-copyfromtexture?language=objc.
|
||||
func (bce BlitCommandEncoder) CopyFromTexture(sourceTexture Texture, sourceSlice int, sourceLevel int, sourceOrigin Origin, sourceSize Size, destinationTexture Texture, destinationSlice int, destinationLevel int, destinationOrigin Origin) {
|
||||
// copyFromTexture requires so many arguments that Send doesn't work (#3135).
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:@QQ{MTLOrigin=qqq}{MTLSize=qqq}@QQ{MTLOrigin=qqq}"))
|
||||
inv.SetTarget(bce.commandEncoder)
|
||||
inv.SetSelector(sel_copyFromTexture_sourceSlice_sourceLevel_sourceOrigin_sourceSize_toTexture_destinationSlice_destinationLevel_destinationOrigin)
|
||||
@@ -1010,15 +1021,15 @@ func (bce BlitCommandEncoder) CopyFromTexture(sourceTexture Texture, sourceSlice
|
||||
|
||||
// Library is a collection of compiled graphics or compute functions.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtllibrary.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtllibrary?language=objc.
|
||||
type Library struct {
|
||||
library objc.ID
|
||||
}
|
||||
|
||||
// MakeFunction returns a pre-compiled, non-specialized function.
|
||||
// NewFunctionWithName returns a pre-compiled, non-specialized function.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtllibrary/1515524-makefunction.
|
||||
func (l Library) MakeFunction(name string) (Function, error) {
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtllibrary/1515524-newfunctionwithname?language=objc.
|
||||
func (l Library) NewFunctionWithName(name string) (Function, error) {
|
||||
f := l.library.Send(sel_newFunctionWithName,
|
||||
cocoa.NSString_alloc().InitWithUTF8String(name).ID,
|
||||
)
|
||||
@@ -1028,10 +1039,14 @@ func (l Library) MakeFunction(name string) (Function, error) {
|
||||
return Function{f}, nil
|
||||
}
|
||||
|
||||
func (l Library) Release() {
|
||||
l.library.Send(sel_release)
|
||||
}
|
||||
|
||||
// Texture is a memory allocation for storing formatted
|
||||
// image data that is accessible to the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture?language=objc.
|
||||
type Texture struct {
|
||||
texture objc.ID
|
||||
}
|
||||
@@ -1042,7 +1057,9 @@ func NewTexture(texture objc.ID) Texture {
|
||||
}
|
||||
|
||||
// resource implements the Resource interface.
|
||||
func (t Texture) resource() unsafe.Pointer { return *(*unsafe.Pointer)(unsafe.Pointer(&t.texture)) }
|
||||
func (t Texture) resource() unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&t.texture))
|
||||
}
|
||||
|
||||
func (t Texture) Release() {
|
||||
t.texture.Send(sel_release)
|
||||
@@ -1051,42 +1068,28 @@ func (t Texture) Release() {
|
||||
// GetBytes copies a block of pixels from the storage allocation of texture
|
||||
// slice zero into system memory at a specified address.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515751-getbytes.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515751-getbytes?language=objc.
|
||||
func (t Texture) GetBytes(pixelBytes *byte, bytesPerRow uintptr, region Region, level int) {
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:^vQ{MTLRegion={MTLOrigin=qqq}{MTLSize=qqq}}Q"))
|
||||
inv.SetTarget(t.texture)
|
||||
inv.SetSelector(sel_getBytes_bytesPerRow_fromRegion_mipmapLevel)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(&pixelBytes), 2)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(&bytesPerRow), 3)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(®ion), 4)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(&level), 5)
|
||||
inv.Invoke()
|
||||
t.texture.Send(sel_getBytes_bytesPerRow_fromRegion_mipmapLevel, pixelBytes, bytesPerRow, region, level)
|
||||
}
|
||||
|
||||
// ReplaceRegion copies a block of pixels from the caller's pointer into the storage allocation for slice 0 of a texture.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515464-replaceregion
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515464-replaceregion?language=objc.
|
||||
func (t Texture) ReplaceRegion(region Region, level int, pixelBytes unsafe.Pointer, bytesPerRow int) {
|
||||
inv := cocoa.NSInvocation_invocationWithMethodSignature(cocoa.NSMethodSignature_signatureWithObjCTypes("v@:{MTLRegion={MTLOrigin=qqq}{MTLSize=qqq}}Q^vQ"))
|
||||
inv.SetTarget(t.texture)
|
||||
inv.SetSelector(sel_replaceRegion_mipmapLevel_withBytes_bytesPerRow)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(®ion), 2)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(&level), 3)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(&pixelBytes), 4)
|
||||
inv.SetArgumentAtIndex(unsafe.Pointer(&bytesPerRow), 5)
|
||||
inv.Invoke()
|
||||
t.texture.Send(sel_replaceRegion_mipmapLevel_withBytes_bytesPerRow, region, level, pixelBytes, bytesPerRow)
|
||||
}
|
||||
|
||||
// Width is the width of the texture image for the base level mipmap, in pixels.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515339-width
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515339-width?language=objc.
|
||||
func (t Texture) Width() int {
|
||||
return int(t.texture.Send(sel_width))
|
||||
}
|
||||
|
||||
// Height is the height of the texture image for the base level mipmap, in pixels.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515938-height
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtltexture/1515938-height?language=objc.
|
||||
func (t Texture) Height() int {
|
||||
return int(t.texture.Send(sel_height))
|
||||
}
|
||||
@@ -1094,13 +1097,19 @@ func (t Texture) Height() int {
|
||||
// Buffer is a memory allocation for storing unformatted data
|
||||
// that is accessible to the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlbuffer.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlbuffer?language=objc.
|
||||
type Buffer struct {
|
||||
buffer objc.ID
|
||||
}
|
||||
|
||||
func (b Buffer) resource() unsafe.Pointer { return *(*unsafe.Pointer)(unsafe.Pointer(&b.buffer)) }
|
||||
// resource implements the Resource interface.
|
||||
func (b Buffer) resource() unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&b.buffer))
|
||||
}
|
||||
|
||||
// Length returns the logical size of the buffer, in bytes.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlbuffer/1515373-length?language=objc.
|
||||
func (b Buffer) Length() uintptr {
|
||||
return uintptr(b.buffer.Send(sel_length))
|
||||
}
|
||||
@@ -1121,13 +1130,9 @@ func (b Buffer) Release() {
|
||||
b.buffer.Send(sel_release)
|
||||
}
|
||||
|
||||
func (b Buffer) Native() unsafe.Pointer {
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&b.buffer))
|
||||
}
|
||||
|
||||
// Function represents a programmable graphics or compute function executed by the GPU.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlfunction.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlfunction?language=objc.
|
||||
type Function struct {
|
||||
function objc.ID
|
||||
}
|
||||
@@ -1139,7 +1144,7 @@ func (f Function) Release() {
|
||||
// RenderPipelineState contains the graphics functions
|
||||
// and configuration state used in a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinestate.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlrenderpipelinestate?language=objc.
|
||||
type RenderPipelineState struct {
|
||||
renderPipelineState objc.ID
|
||||
}
|
||||
@@ -1151,7 +1156,7 @@ func (r RenderPipelineState) Release() {
|
||||
// Region is a rectangular block of pixels in an image or texture,
|
||||
// defined by its upper-left corner and its size.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlregion.
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlregion?language=objc.
|
||||
type Region struct {
|
||||
Origin Origin // The location of the upper-left corner of the block.
|
||||
Size Size // The size of the block.
|
||||
@@ -1160,25 +1165,36 @@ type Region struct {
|
||||
// Origin represents the location of a pixel in an image or texture relative
|
||||
// to the upper-left corner, whose coordinates are (0, 0).
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlorigin.
|
||||
type Origin struct{ X, Y, Z int }
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlorigin?language=objc.
|
||||
type Origin struct {
|
||||
X int
|
||||
Y int
|
||||
Z int
|
||||
}
|
||||
|
||||
// Size represents the set of dimensions that declare the size of an object,
|
||||
// such as an image, texture, threadgroup, or grid.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlsize.
|
||||
type Size struct{ Width, Height, Depth int }
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlsize?language=objc.
|
||||
type Size struct {
|
||||
Width int
|
||||
Height int
|
||||
Depth int
|
||||
}
|
||||
|
||||
// RegionMake2D returns a 2D, rectangular region for image or texture data.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/1515675-mtlregionmake2d.
|
||||
// Reference: https://developer.apple.com/documentation/metal/1515675-mtlregionmake2d?language=objc.
|
||||
func RegionMake2D(x, y, width, height int) Region {
|
||||
return Region{
|
||||
Origin: Origin{x, y, 0},
|
||||
Size: Size{width, height, 1},
|
||||
Origin: Origin{X: x, Y: y, Z: 0},
|
||||
Size: Size{Width: width, Height: height, Depth: 1},
|
||||
}
|
||||
}
|
||||
|
||||
// Viewport is a 3D rectangular region for the viewport clipping.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlviewport?language=objc.
|
||||
type Viewport struct {
|
||||
OriginX float64
|
||||
OriginY float64
|
||||
@@ -1188,9 +1204,9 @@ type Viewport struct {
|
||||
ZFar float64
|
||||
}
|
||||
|
||||
// ScissorRect represents a rectangle for the scissor fragment test.
|
||||
// ScissorRect is a rectangle for the scissor fragment test.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlscissorrect
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlscissorrect?language=objc.
|
||||
type ScissorRect struct {
|
||||
X int
|
||||
Y int
|
||||
@@ -1200,7 +1216,7 @@ type ScissorRect struct {
|
||||
|
||||
// DepthStencilState is a depth and stencil state object that specifies the depth and stencil configuration and operations used in a render pass.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldepthstencilstate
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldepthstencilstate?language=objc.
|
||||
type DepthStencilState struct {
|
||||
depthStencilState objc.ID
|
||||
}
|
||||
@@ -1211,7 +1227,7 @@ func (d DepthStencilState) Release() {
|
||||
|
||||
// DepthStencilDescriptor is an object that configures new MTLDepthStencilState objects.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldepthstencildescriptor
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtldepthstencildescriptor?language=objc.
|
||||
type DepthStencilDescriptor struct {
|
||||
// BackFaceStencil is the stencil descriptor for back-facing primitives.
|
||||
BackFaceStencil StencilDescriptor
|
||||
@@ -1222,7 +1238,7 @@ type DepthStencilDescriptor struct {
|
||||
|
||||
// StencilDescriptor is an object that defines the front-facing or back-facing stencil operations of a depth and stencil state object.
|
||||
//
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstencildescriptor
|
||||
// Reference: https://developer.apple.com/documentation/metal/mtlstencildescriptor?language=objc.
|
||||
type StencilDescriptor struct {
|
||||
// StencilFailureOperation is the operation that is performed to update the values in the stencil attachment when the stencil test fails.
|
||||
StencilFailureOperation StencilOperation
|
||||
|
||||
Generated
Vendored
+65
-9
@@ -16,6 +16,7 @@ package metal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
|
||||
@@ -23,6 +24,37 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir/msl"
|
||||
)
|
||||
|
||||
type precompiledLibraries struct {
|
||||
binaries map[shaderir.SourceHash][]byte
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
func (c *precompiledLibraries) put(hash shaderir.SourceHash, bin []byte) {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
|
||||
if c.binaries == nil {
|
||||
c.binaries = map[shaderir.SourceHash][]byte{}
|
||||
}
|
||||
if _, ok := c.binaries[hash]; ok {
|
||||
panic(fmt.Sprintf("metal: the precompiled library for the hash %s is already registered", hash.String()))
|
||||
}
|
||||
c.binaries[hash] = bin
|
||||
}
|
||||
|
||||
func (c *precompiledLibraries) get(hash shaderir.SourceHash) []byte {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
|
||||
return c.binaries[hash]
|
||||
}
|
||||
|
||||
var thePrecompiledLibraries precompiledLibraries
|
||||
|
||||
func RegisterPrecompiledLibrary(source []byte, bin []byte) {
|
||||
thePrecompiledLibraries.put(shaderir.CalcSourceHash(source), bin)
|
||||
}
|
||||
|
||||
type shaderRpsKey struct {
|
||||
blend graphicsdriver.Blend
|
||||
stencilMode stencilMode
|
||||
@@ -33,9 +65,12 @@ type Shader struct {
|
||||
id graphicsdriver.ShaderID
|
||||
|
||||
ir *shaderir.Program
|
||||
lib mtl.Library
|
||||
fs mtl.Function
|
||||
vs mtl.Function
|
||||
rpss map[shaderRpsKey]mtl.RenderPipelineState
|
||||
|
||||
libraryPrecompiled bool
|
||||
}
|
||||
|
||||
func newShader(device mtl.Device, id graphicsdriver.ShaderID, program *shaderir.Program) (*Shader, error) {
|
||||
@@ -60,21 +95,42 @@ func (s *Shader) Dispose() {
|
||||
}
|
||||
s.vs.Release()
|
||||
s.fs.Release()
|
||||
// Do not release s.lib if this is precompiled. This is a shared precompiled library.
|
||||
if !s.libraryPrecompiled {
|
||||
s.lib.Release()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Shader) init(device mtl.Device) error {
|
||||
src := msl.Compile(s.ir)
|
||||
lib, err := device.MakeLibrary(src, mtl.CompileOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("metal: device.MakeLibrary failed: %w, source: %s", err, src)
|
||||
var src string
|
||||
if libBin := thePrecompiledLibraries.get(s.ir.SourceHash); len(libBin) > 0 {
|
||||
lib, err := device.NewLibraryWithData(libBin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.lib = lib
|
||||
} else {
|
||||
src = msl.Compile(s.ir)
|
||||
lib, err := device.NewLibraryWithSource(src, mtl.CompileOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("metal: device.MakeLibrary failed: %w, source: %s", err, src)
|
||||
}
|
||||
s.lib = lib
|
||||
}
|
||||
vs, err := lib.MakeFunction(msl.VertexName)
|
||||
|
||||
vs, err := s.lib.NewFunctionWithName(msl.VertexName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w, source: %s", err, src)
|
||||
if src != "" {
|
||||
return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w, source: %s", err, src)
|
||||
}
|
||||
return fmt.Errorf("metal: lib.MakeFunction for vertex failed: %w", err)
|
||||
}
|
||||
fs, err := lib.MakeFunction(msl.FragmentName)
|
||||
fs, err := s.lib.NewFunctionWithName(msl.FragmentName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w, source: %s", err, src)
|
||||
if src != "" {
|
||||
return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w, source: %s", err, src)
|
||||
}
|
||||
return fmt.Errorf("metal: lib.MakeFunction for fragment failed: %w", err)
|
||||
}
|
||||
s.fs = fs
|
||||
s.vs = vs
|
||||
@@ -120,7 +176,7 @@ func (s *Shader) RenderPipelineState(view *view, blend graphicsdriver.Blend, ste
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskNone
|
||||
}
|
||||
|
||||
rps, err := view.getMTLDevice().MakeRenderPipelineState(rpld)
|
||||
rps, err := view.getMTLDevice().NewRenderPipelineStateWithDescriptor(rpld)
|
||||
if err != nil {
|
||||
return mtl.RenderPipelineState{}, err
|
||||
}
|
||||
|
||||
Generated
Vendored
+56
-10
@@ -15,12 +15,22 @@
|
||||
package metal
|
||||
|
||||
import (
|
||||
"runtime/cgo"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/cocoa"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/ca"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
|
||||
)
|
||||
|
||||
// maximumDrawableCount is the maximum number of drawable objects.
|
||||
//
|
||||
// Always use 3 for macOS (#2880, #2883, #3278).
|
||||
// At least, this should work with MacBook Pro 2020 (Intel) and MacBook Pro 2023 (M3).
|
||||
const maximumDrawableCount = 3
|
||||
|
||||
type view struct {
|
||||
window uintptr
|
||||
uiview uintptr
|
||||
@@ -32,6 +42,19 @@ type view struct {
|
||||
ml ca.MetalLayer
|
||||
|
||||
once sync.Once
|
||||
|
||||
caDisplayLink uintptr
|
||||
metalDisplayLink uintptr
|
||||
|
||||
// The following members are used only with CAMetalDisplayLink.
|
||||
drawableCh chan ca.MetalDrawable
|
||||
drawableDoneCh chan struct{}
|
||||
drawableTimer *time.Timer
|
||||
metalDisplayLinkRunLoop cocoa.NSRunLoop
|
||||
|
||||
// The following members are used only with CADisplayLink.
|
||||
handleToSelf cgo.Handle
|
||||
fence *fence
|
||||
}
|
||||
|
||||
func (v *view) setDrawableSize(width, height int) {
|
||||
@@ -58,10 +81,10 @@ func (v *view) colorPixelFormat() mtl.PixelFormat {
|
||||
return v.ml.PixelFormat()
|
||||
}
|
||||
|
||||
func (v *view) initialize(device mtl.Device) error {
|
||||
func (v *view) initialize(device mtl.Device, colorSpace graphicsdriver.ColorSpace) error {
|
||||
v.device = device
|
||||
|
||||
ml, err := ca.MakeMetalLayer()
|
||||
ml, err := ca.NewMetalLayer(colorSpace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -83,16 +106,39 @@ func (v *view) initialize(device mtl.Device) error {
|
||||
// nextDrawable took more than one second if the window has other controls like NSTextView (#1029).
|
||||
v.ml.SetPresentsWithTransaction(false)
|
||||
|
||||
v.ml.SetMaximumDrawableCount(v.maximumDrawableCount())
|
||||
v.ml.SetMaximumDrawableCount(maximumDrawableCount)
|
||||
|
||||
if err := v.initializeOS(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *view) nextDrawable() ca.MetalDrawable {
|
||||
d, err := v.ml.NextDrawable()
|
||||
if err != nil {
|
||||
// Drawable is nil. This can happen at the initial state. Let's wait and see.
|
||||
return ca.MetalDrawable{}
|
||||
}
|
||||
return d
|
||||
type fence struct {
|
||||
value uint64
|
||||
lastValue uint64
|
||||
cond *sync.Cond
|
||||
}
|
||||
|
||||
func newFence() *fence {
|
||||
return &fence{
|
||||
cond: sync.NewCond(&sync.Mutex{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fence) wait() {
|
||||
f.cond.L.Lock()
|
||||
defer f.cond.L.Unlock()
|
||||
for f.lastValue >= f.value {
|
||||
f.cond.Wait()
|
||||
}
|
||||
f.lastValue = f.value
|
||||
}
|
||||
|
||||
func (f *fence) advance() {
|
||||
f.cond.L.Lock()
|
||||
defer f.cond.L.Unlock()
|
||||
f.value++
|
||||
f.cond.Broadcast()
|
||||
}
|
||||
|
||||
Generated
Vendored
+21
-3
@@ -22,11 +22,15 @@ package metal
|
||||
//
|
||||
// #import <UIKit/UIKit.h>
|
||||
//
|
||||
// #cgo noescape addSublayer
|
||||
// #cgo nocallback addSublayer
|
||||
// static void addSublayer(void* view, void* sublayer) {
|
||||
// CALayer* layer = ((UIView*)view).layer;
|
||||
// [layer addSublayer:(CALayer*)sublayer];
|
||||
// }
|
||||
//
|
||||
// #cgo noescape setFrame
|
||||
// #cgo nocallback setFrame
|
||||
// static void setFrame(void* cametal, void* uiview) {
|
||||
// __block CGSize size;
|
||||
// dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
@@ -39,6 +43,7 @@ import "C"
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/ca"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
|
||||
)
|
||||
|
||||
@@ -65,7 +70,20 @@ const (
|
||||
resourceStorageMode = mtl.ResourceStorageModeShared
|
||||
)
|
||||
|
||||
func (v *view) maximumDrawableCount() int {
|
||||
// TODO: Is 2 available for iOS?
|
||||
return 3
|
||||
func (v *view) nextDrawable() ca.MetalDrawable {
|
||||
d, err := v.ml.NextDrawable()
|
||||
if err != nil {
|
||||
// Drawable is nil. This can happen at the initial state. Let's wait and see.
|
||||
return ca.MetalDrawable{}
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func (v *view) finishDrawableUsage() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
func (v *view) initializeOS() error {
|
||||
// Do nothing.
|
||||
return nil
|
||||
}
|
||||
|
||||
Generated
Vendored
+15
-22
@@ -17,14 +17,14 @@
|
||||
package metal
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/ebitengine/purego/objc"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/cocoa"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal/mtl"
|
||||
)
|
||||
|
||||
const kCVReturnSuccess = 0
|
||||
|
||||
func (v *view) setWindow(window uintptr) {
|
||||
// NSView can be updated e.g., fullscreen-state is switched.
|
||||
v.window = window
|
||||
@@ -36,8 +36,6 @@ func (v *view) setUIView(uiview uintptr) {
|
||||
}
|
||||
|
||||
func (v *view) update() {
|
||||
v.ml.SetMaximumDrawableCount(v.maximumDrawableCount())
|
||||
|
||||
if !v.windowChanged {
|
||||
return
|
||||
}
|
||||
@@ -55,25 +53,20 @@ const (
|
||||
resourceStorageMode = mtl.ResourceStorageModeManaged
|
||||
)
|
||||
|
||||
func (v *view) maximumDrawableCount() int {
|
||||
// Note that the architecture might not be the true reason of the issues (#2880, #2883).
|
||||
// Hajime tested only MacBook Pro 2020 (Intel) and MacBook Pro 2023 (M3).
|
||||
|
||||
// Use 3 for Intel Mac and iOS. With 2, There are some situations that the FPS becomes half, or the FPS becomes too low (#2880).
|
||||
if runtime.GOARCH == "amd64" {
|
||||
return 3
|
||||
func (v *view) initializeOS() error {
|
||||
if err := v.initDisplayLink(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Use 3 in fullscren.
|
||||
// Though this might degrade FPS, this is necessary to avoid mysterious rendering delays.
|
||||
if v.isFullscreen() {
|
||||
return 3
|
||||
}
|
||||
|
||||
// Use 2 for a Wnidow to avoid mysterious blinking (#2883).
|
||||
return 2
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *view) isFullscreen() bool {
|
||||
return cocoa.NSWindow{ID: objc.ID(v.window)}.StyleMask()&cocoa.NSWindowStyleMaskFullScreen != 0
|
||||
func (v *view) waitForDisplayLinkOutputCallback() {
|
||||
if v.caDisplayLink == 0 && v.metalDisplayLink == 0 {
|
||||
return
|
||||
}
|
||||
if v.caDisplayLink == 0 && v.vsyncDisabled {
|
||||
// TODO: nextDrawable still waits for the next drawable available, so this should be fixed not to wait.
|
||||
return
|
||||
}
|
||||
v.fence.wait()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user