github.com/Seikaijyu/gio@v0.0.1/app/metal_darwin.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 //go:build !nometal 4 // +build !nometal 5 6 package app 7 8 import ( 9 "errors" 10 11 "github.com/Seikaijyu/gio/gpu" 12 ) 13 14 /* 15 #cgo CFLAGS: -Werror -xobjective-c -fobjc-arc 16 #cgo LDFLAGS: -framework QuartzCore -framework Metal 17 18 #import <Metal/Metal.h> 19 #import <QuartzCore/CAMetalLayer.h> 20 21 #include <CoreFoundation/CoreFoundation.h> 22 23 static CFTypeRef createMetalDevice(void) { 24 @autoreleasepool { 25 id<MTLDevice> dev = MTLCreateSystemDefaultDevice(); 26 return CFBridgingRetain(dev); 27 } 28 } 29 30 static void setupLayer(CFTypeRef layerRef, CFTypeRef devRef) { 31 @autoreleasepool { 32 CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef; 33 id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; 34 layer.device = dev; 35 // Package gpu assumes an sRGB-encoded framebuffer. 36 layer.pixelFormat = MTLPixelFormatBGRA8Unorm_sRGB; 37 if (@available(iOS 11.0, *)) { 38 // Never let nextDrawable time out and return nil. 39 layer.allowsNextDrawableTimeout = NO; 40 } 41 } 42 } 43 44 static CFTypeRef nextDrawable(CFTypeRef layerRef) { 45 @autoreleasepool { 46 CAMetalLayer *layer = (__bridge CAMetalLayer *)layerRef; 47 return CFBridgingRetain([layer nextDrawable]); 48 } 49 } 50 51 static CFTypeRef drawableTexture(CFTypeRef drawableRef) { 52 @autoreleasepool { 53 id<CAMetalDrawable> drawable = (__bridge id<CAMetalDrawable>)drawableRef; 54 return CFBridgingRetain(drawable.texture); 55 } 56 } 57 58 static void presentDrawable(CFTypeRef queueRef, CFTypeRef drawableRef) { 59 @autoreleasepool { 60 id<MTLDrawable> drawable = (__bridge id<MTLDrawable>)drawableRef; 61 id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef; 62 id<MTLCommandBuffer> cmdBuffer = [queue commandBuffer]; 63 [cmdBuffer presentDrawable:drawable]; 64 [cmdBuffer commit]; 65 } 66 } 67 68 static CFTypeRef newCommandQueue(CFTypeRef devRef) { 69 @autoreleasepool { 70 id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; 71 return CFBridgingRetain([dev newCommandQueue]); 72 } 73 } 74 */ 75 import "C" 76 77 type mtlContext struct { 78 dev C.CFTypeRef 79 view C.CFTypeRef 80 layer C.CFTypeRef 81 queue C.CFTypeRef 82 drawable C.CFTypeRef 83 texture C.CFTypeRef 84 } 85 86 func newMtlContext(w *window) (*mtlContext, error) { 87 dev := C.createMetalDevice() 88 if dev == 0 { 89 return nil, errors.New("metal: MTLCreateSystemDefaultDevice failed") 90 } 91 view := w.contextView() 92 layer := getMetalLayer(view) 93 if layer == 0 { 94 C.CFRelease(dev) 95 return nil, errors.New("metal: CAMetalLayer construction failed") 96 } 97 queue := C.newCommandQueue(dev) 98 if layer == 0 { 99 C.CFRelease(dev) 100 C.CFRelease(layer) 101 return nil, errors.New("metal: [MTLDevice newCommandQueue] failed") 102 } 103 C.setupLayer(layer, dev) 104 c := &mtlContext{ 105 dev: dev, 106 view: view, 107 layer: layer, 108 queue: queue, 109 } 110 return c, nil 111 } 112 113 func (c *mtlContext) RenderTarget() (gpu.RenderTarget, error) { 114 if c.drawable != 0 || c.texture != 0 { 115 return nil, errors.New("metal:a previous RenderTarget wasn't Presented") 116 } 117 c.drawable = C.nextDrawable(c.layer) 118 if c.drawable == 0 { 119 return nil, errors.New("metal: [CAMetalLayer nextDrawable] failed") 120 } 121 c.texture = C.drawableTexture(c.drawable) 122 if c.texture == 0 { 123 return nil, errors.New("metal: CADrawable.texture is nil") 124 } 125 return gpu.MetalRenderTarget{ 126 Texture: uintptr(c.texture), 127 }, nil 128 } 129 130 func (c *mtlContext) API() gpu.API { 131 return gpu.Metal{ 132 Device: uintptr(c.dev), 133 Queue: uintptr(c.queue), 134 PixelFormat: int(C.MTLPixelFormatBGRA8Unorm_sRGB), 135 } 136 } 137 138 func (c *mtlContext) Release() { 139 C.CFRelease(c.queue) 140 C.CFRelease(c.dev) 141 C.CFRelease(c.layer) 142 if c.drawable != 0 { 143 C.CFRelease(c.drawable) 144 } 145 if c.texture != 0 { 146 C.CFRelease(c.texture) 147 } 148 *c = mtlContext{} 149 } 150 151 func (c *mtlContext) Present() error { 152 C.CFRelease(c.texture) 153 c.texture = 0 154 C.presentDrawable(c.queue, c.drawable) 155 C.CFRelease(c.drawable) 156 c.drawable = 0 157 return nil 158 } 159 160 func (c *mtlContext) Lock() error { 161 return nil 162 } 163 164 func (c *mtlContext) Unlock() {} 165 166 func (c *mtlContext) Refresh() error { 167 resizeDrawable(c.view, c.layer) 168 return nil 169 } 170 171 func (w *window) NewContext() (context, error) { 172 return newMtlContext(w) 173 }