gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/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 "gioui.org/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 commit]; 64 [cmdBuffer waitUntilScheduled]; 65 [drawable present]; 66 } 67 } 68 69 static CFTypeRef newCommandQueue(CFTypeRef devRef) { 70 @autoreleasepool { 71 id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef; 72 return CFBridgingRetain([dev newCommandQueue]); 73 } 74 } 75 */ 76 import "C" 77 78 type mtlContext struct { 79 dev C.CFTypeRef 80 view C.CFTypeRef 81 layer C.CFTypeRef 82 queue C.CFTypeRef 83 drawable C.CFTypeRef 84 texture C.CFTypeRef 85 } 86 87 func newMtlContext(w *window) (*mtlContext, error) { 88 dev := C.createMetalDevice() 89 if dev == 0 { 90 return nil, errors.New("metal: MTLCreateSystemDefaultDevice failed") 91 } 92 view := w.contextView() 93 layer := getMetalLayer(view) 94 if layer == 0 { 95 C.CFRelease(dev) 96 return nil, errors.New("metal: CAMetalLayer construction failed") 97 } 98 queue := C.newCommandQueue(dev) 99 if layer == 0 { 100 C.CFRelease(dev) 101 C.CFRelease(layer) 102 return nil, errors.New("metal: [MTLDevice newCommandQueue] failed") 103 } 104 C.setupLayer(layer, dev) 105 c := &mtlContext{ 106 dev: dev, 107 view: view, 108 layer: layer, 109 queue: queue, 110 } 111 return c, nil 112 } 113 114 func (c *mtlContext) RenderTarget() (gpu.RenderTarget, error) { 115 if c.drawable != 0 || c.texture != 0 { 116 return nil, errors.New("metal:a previous RenderTarget wasn't Presented") 117 } 118 c.drawable = C.nextDrawable(c.layer) 119 if c.drawable == 0 { 120 return nil, errors.New("metal: [CAMetalLayer nextDrawable] failed") 121 } 122 c.texture = C.drawableTexture(c.drawable) 123 if c.texture == 0 { 124 return nil, errors.New("metal: CADrawable.texture is nil") 125 } 126 return gpu.MetalRenderTarget{ 127 Texture: uintptr(c.texture), 128 }, nil 129 } 130 131 func (c *mtlContext) API() gpu.API { 132 return gpu.Metal{ 133 Device: uintptr(c.dev), 134 Queue: uintptr(c.queue), 135 PixelFormat: int(C.MTLPixelFormatBGRA8Unorm_sRGB), 136 } 137 } 138 139 func (c *mtlContext) Release() { 140 C.CFRelease(c.queue) 141 C.CFRelease(c.dev) 142 C.CFRelease(c.layer) 143 if c.drawable != 0 { 144 C.CFRelease(c.drawable) 145 } 146 if c.texture != 0 { 147 C.CFRelease(c.texture) 148 } 149 *c = mtlContext{} 150 } 151 152 func (c *mtlContext) Present() error { 153 C.CFRelease(c.texture) 154 c.texture = 0 155 C.presentDrawable(c.queue, c.drawable) 156 C.CFRelease(c.drawable) 157 c.drawable = 0 158 return nil 159 } 160 161 func (c *mtlContext) Lock() error { 162 return nil 163 } 164 165 func (c *mtlContext) Unlock() {} 166 167 func (c *mtlContext) Refresh() error { 168 resizeDrawable(c.view, c.layer) 169 return nil 170 } 171 172 func (w *window) NewContext() (context, error) { 173 return newMtlContext(w) 174 }