metal_darwin.go 4.0 KB

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