mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-03 16:35:36 +00:00
a4a2d517e7
We shouldn't need more than 2 drawables, but changing the count from the default of 3 introduces framerate lags in fullscreen mode. This changes leaves the drawable count alone. No good deed goes unpunished. Signed-off-by: Elias Naur <mail@eliasnaur.com>
175 lines
3.9 KiB
Go
175 lines
3.9 KiB
Go
// SPDX-License-Identifier: Unlicense OR MIT
|
|
|
|
//go:build !nometal
|
|
// +build !nometal
|
|
|
|
package wm
|
|
|
|
import (
|
|
"errors"
|
|
"unsafe"
|
|
|
|
"gioui.org/gpu"
|
|
)
|
|
|
|
/*
|
|
#cgo CFLAGS: -Werror -xobjective-c -fmodules -fobjc-arc
|
|
|
|
@import Metal;
|
|
@import QuartzCore.CAMetalLayer;
|
|
|
|
#include <CoreFoundation/CoreFoundation.h>
|
|
|
|
static CFTypeRef createMetalDevice(void) {
|
|
@autoreleasepool {
|
|
id<MTLDevice> dev = MTLCreateSystemDefaultDevice();
|
|
return CFBridgingRetain(dev);
|
|
}
|
|
}
|
|
|
|
static void setupLayer(CFTypeRef layerRef, CFTypeRef devRef) {
|
|
@autoreleasepool {
|
|
CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef;
|
|
id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
|
|
layer.device = dev;
|
|
// Package gpu assumes an sRGB-encoded framebuffer.
|
|
layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB;
|
|
if (@available(iOS 11.0, *)) {
|
|
// Never let nextDrawable time out and return nil.
|
|
layer.allowsNextDrawableTimeout = NO;
|
|
}
|
|
}
|
|
}
|
|
|
|
static CFTypeRef nextDrawable(CFTypeRef layerRef) {
|
|
@autoreleasepool {
|
|
CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef;
|
|
return CFBridgingRetain([layer nextDrawable]);
|
|
}
|
|
}
|
|
|
|
static CFTypeRef drawableTexture(CFTypeRef drawableRef) {
|
|
@autoreleasepool {
|
|
id<CAMetalDrawable> drawable = (__bridge id<CAMetalDrawable>)drawableRef;
|
|
return CFBridgingRetain(drawable.texture);
|
|
}
|
|
}
|
|
|
|
static void presentDrawable(CFTypeRef queueRef, CFTypeRef drawableRef) {
|
|
@autoreleasepool {
|
|
id<MTLDrawable> drawable = (__bridge id<MTLDrawable>)drawableRef;
|
|
id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef;
|
|
id<MTLCommandBuffer> cmdBuffer = [queue commandBuffer];
|
|
[cmdBuffer presentDrawable:drawable];
|
|
[cmdBuffer commit];
|
|
[cmdBuffer waitUntilCompleted];
|
|
}
|
|
}
|
|
|
|
static CFTypeRef newCommandQueue(CFTypeRef devRef) {
|
|
@autoreleasepool {
|
|
id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
|
|
return CFBridgingRetain([dev newCommandQueue]);
|
|
}
|
|
}
|
|
*/
|
|
import "C"
|
|
|
|
type mtlContext struct {
|
|
dev C.CFTypeRef
|
|
view C.CFTypeRef
|
|
layer C.CFTypeRef
|
|
queue C.CFTypeRef
|
|
drawable C.CFTypeRef
|
|
texture C.CFTypeRef
|
|
}
|
|
|
|
func newMtlContext(w *window) (*mtlContext, error) {
|
|
dev := C.createMetalDevice()
|
|
if dev == 0 {
|
|
return nil, errors.New("metal: MTLCreateSystemDefaultDevice failed")
|
|
}
|
|
view := w.contextView()
|
|
layer := getMetalLayer(view)
|
|
if layer == 0 {
|
|
C.CFRelease(dev)
|
|
return nil, errors.New("metal: CAMetalLayer construction failed")
|
|
}
|
|
queue := C.newCommandQueue(dev)
|
|
if layer == 0 {
|
|
C.CFRelease(dev)
|
|
C.CFRelease(layer)
|
|
return nil, errors.New("metal: [MTLDevice newCommandQueue] failed")
|
|
}
|
|
C.setupLayer(layer, dev)
|
|
c := &mtlContext{
|
|
dev: dev,
|
|
view: view,
|
|
layer: layer,
|
|
queue: queue,
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func (c *mtlContext) RenderTarget() gpu.RenderTarget {
|
|
if c.drawable != 0 || c.texture != 0 {
|
|
panic("a previous RenderTarget wasn't Presented")
|
|
}
|
|
c.drawable = C.nextDrawable(c.layer)
|
|
if c.drawable == 0 {
|
|
panic("metal: [CAMetalLayer nextDrawable] failed")
|
|
}
|
|
c.texture = C.drawableTexture(c.drawable)
|
|
if c.texture == 0 {
|
|
panic("metal: CADrawable.texture is nil")
|
|
}
|
|
return gpu.MetalRenderTarget{
|
|
Texture: unsafe.Pointer(c.texture),
|
|
}
|
|
}
|
|
|
|
func (c *mtlContext) API() gpu.API {
|
|
return gpu.Metal{
|
|
Device: unsafe.Pointer(c.dev),
|
|
Queue: unsafe.Pointer(c.queue),
|
|
PixelFormat: int(C.MTLPixelFormatBGRA8Unorm_sRGB),
|
|
}
|
|
}
|
|
|
|
func (c *mtlContext) Release() {
|
|
C.CFRelease(c.queue)
|
|
C.CFRelease(c.dev)
|
|
C.CFRelease(c.layer)
|
|
if c.drawable != 0 {
|
|
C.CFRelease(c.drawable)
|
|
}
|
|
if c.texture != 0 {
|
|
C.CFRelease(c.texture)
|
|
}
|
|
*c = mtlContext{}
|
|
}
|
|
|
|
func (c *mtlContext) Present() error {
|
|
C.CFRelease(c.texture)
|
|
c.texture = 0
|
|
C.presentDrawable(c.queue, c.drawable)
|
|
C.CFRelease(c.drawable)
|
|
c.drawable = 0
|
|
return nil
|
|
}
|
|
|
|
func (c *mtlContext) Lock() error {
|
|
return nil
|
|
}
|
|
|
|
func (c *mtlContext) Unlock() {}
|
|
|
|
func (c *mtlContext) Refresh() error {
|
|
resizeDrawable(c.view, c.layer)
|
|
return nil
|
|
}
|
|
|
|
func (w *window) NewContext() (Context, error) {
|
|
return newMtlContext(w)
|
|
}
|