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  }