gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/gpu/internal/metal/metal_darwin.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package metal
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"image"
     9  	"unsafe"
    10  
    11  	"gioui.org/gpu/internal/driver"
    12  	"gioui.org/shader"
    13  )
    14  
    15  /*
    16  #cgo CFLAGS: -Werror -xobjective-c -fobjc-arc
    17  #cgo LDFLAGS: -framework CoreGraphics -framework Metal -framework Foundation
    18  
    19  #include <CoreFoundation/CoreFoundation.h>
    20  #include <Metal/Metal.h>
    21  
    22  typedef struct {
    23  	void *addr;
    24  	NSUInteger size;
    25  } slice;
    26  
    27  static CFTypeRef queueNewBuffer(CFTypeRef queueRef) {
    28  	@autoreleasepool {
    29  		id<MTLCommandQueue> queue = (__bridge id<MTLCommandQueue>)queueRef;
    30  		return CFBridgingRetain([queue commandBuffer]);
    31  	}
    32  }
    33  
    34  static void cmdBufferCommit(CFTypeRef cmdBufRef) {
    35  	@autoreleasepool {
    36  		id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
    37  		[cmdBuf commit];
    38  	}
    39  }
    40  
    41  static void cmdBufferWaitUntilCompleted(CFTypeRef cmdBufRef) {
    42  	@autoreleasepool {
    43  		id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
    44  		[cmdBuf waitUntilCompleted];
    45  	}
    46  }
    47  
    48  static CFTypeRef cmdBufferRenderEncoder(CFTypeRef cmdBufRef, CFTypeRef textureRef, MTLLoadAction act, float r, float g, float b, float a) {
    49  	@autoreleasepool {
    50  		id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
    51  		MTLRenderPassDescriptor *desc = [MTLRenderPassDescriptor new];
    52  		desc.colorAttachments[0].texture = (__bridge id<MTLTexture>)textureRef;
    53  		desc.colorAttachments[0].loadAction = act;
    54  		desc.colorAttachments[0].clearColor = MTLClearColorMake(r, g, b, a);
    55  		return CFBridgingRetain([cmdBuf renderCommandEncoderWithDescriptor:desc]);
    56  	}
    57  }
    58  
    59  static CFTypeRef cmdBufferComputeEncoder(CFTypeRef cmdBufRef) {
    60  	@autoreleasepool {
    61  		id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
    62  		return CFBridgingRetain([cmdBuf computeCommandEncoder]);
    63  	}
    64  }
    65  
    66  static CFTypeRef cmdBufferBlitEncoder(CFTypeRef cmdBufRef) {
    67  	@autoreleasepool {
    68  		id<MTLCommandBuffer> cmdBuf = (__bridge id<MTLCommandBuffer>)cmdBufRef;
    69  		return CFBridgingRetain([cmdBuf blitCommandEncoder]);
    70  	}
    71  }
    72  
    73  static void renderEncEnd(CFTypeRef renderEncRef) {
    74  	@autoreleasepool {
    75  		id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
    76  		[enc endEncoding];
    77  	}
    78  }
    79  
    80  static void renderEncViewport(CFTypeRef renderEncRef, MTLViewport viewport) {
    81  	@autoreleasepool {
    82  		id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
    83  		[enc setViewport:viewport];
    84  	}
    85  }
    86  
    87  static void renderEncSetFragmentTexture(CFTypeRef renderEncRef, NSUInteger index, CFTypeRef texRef) {
    88  	@autoreleasepool {
    89  		id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
    90  		id<MTLTexture> tex = (__bridge id<MTLTexture>)texRef;
    91  		[enc setFragmentTexture:tex atIndex:index];
    92  	}
    93  }
    94  
    95  static void renderEncSetFragmentSamplerState(CFTypeRef renderEncRef, NSUInteger index, CFTypeRef samplerRef) {
    96  	@autoreleasepool {
    97  		id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
    98  		id<MTLSamplerState> sampler = (__bridge id<MTLSamplerState>)samplerRef;
    99  		[enc setFragmentSamplerState:sampler atIndex:index];
   100  	}
   101  }
   102  
   103  static void renderEncSetVertexBuffer(CFTypeRef renderEncRef, CFTypeRef bufRef, NSUInteger idx, NSUInteger offset) {
   104  	@autoreleasepool {
   105  		id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
   106  		id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
   107  		[enc setVertexBuffer:buf offset:offset atIndex:idx];
   108  	}
   109  }
   110  
   111  static void renderEncSetFragmentBuffer(CFTypeRef renderEncRef, CFTypeRef bufRef, NSUInteger idx, NSUInteger offset) {
   112  	@autoreleasepool {
   113  		id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
   114  		id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
   115  		[enc setFragmentBuffer:buf offset:offset atIndex:idx];
   116  	}
   117  }
   118  
   119  static void renderEncSetFragmentBytes(CFTypeRef renderEncRef, const void *bytes, NSUInteger length, NSUInteger idx) {
   120  	@autoreleasepool {
   121  		id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
   122  		[enc setFragmentBytes:bytes length:length atIndex:idx];
   123  	}
   124  }
   125  
   126  static void renderEncSetVertexBytes(CFTypeRef renderEncRef, const void *bytes, NSUInteger length, NSUInteger idx) {
   127  	@autoreleasepool {
   128  		id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
   129  		[enc setVertexBytes:bytes length:length atIndex:idx];
   130  	}
   131  }
   132  
   133  static void renderEncSetRenderPipelineState(CFTypeRef renderEncRef, CFTypeRef pipeRef) {
   134  	@autoreleasepool {
   135  		id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
   136  		id<MTLRenderPipelineState> pipe = (__bridge id<MTLRenderPipelineState>)pipeRef;
   137  		[enc setRenderPipelineState:pipe];
   138  	}
   139  }
   140  
   141  static void renderEncDrawPrimitives(CFTypeRef renderEncRef, MTLPrimitiveType type, NSUInteger start, NSUInteger count) {
   142  	@autoreleasepool {
   143  		id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
   144  		[enc drawPrimitives:type vertexStart:start vertexCount:count];
   145  	}
   146  }
   147  
   148  static void renderEncDrawIndexedPrimitives(CFTypeRef renderEncRef, MTLPrimitiveType type, CFTypeRef bufRef, NSUInteger offset, NSUInteger count) {
   149  	@autoreleasepool {
   150  		id<MTLRenderCommandEncoder> enc = (__bridge id<MTLRenderCommandEncoder>)renderEncRef;
   151  		id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
   152  		[enc drawIndexedPrimitives:type indexCount:count indexType:MTLIndexTypeUInt16 indexBuffer:buf indexBufferOffset:offset];
   153  	}
   154  }
   155  
   156  static void computeEncSetPipeline(CFTypeRef computeEncRef, CFTypeRef pipeRef) {
   157  	@autoreleasepool {
   158  		id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
   159  		id<MTLComputePipelineState> pipe = (__bridge id<MTLComputePipelineState>)pipeRef;
   160  		[enc setComputePipelineState:pipe];
   161  	}
   162  }
   163  
   164  static void computeEncSetTexture(CFTypeRef computeEncRef, NSUInteger index, CFTypeRef texRef) {
   165  	@autoreleasepool {
   166  		id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
   167  		id<MTLTexture> tex = (__bridge id<MTLTexture>)texRef;
   168  		[enc setTexture:tex atIndex:index];
   169  	}
   170  }
   171  
   172  static void computeEncEnd(CFTypeRef computeEncRef) {
   173  	@autoreleasepool {
   174  		id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
   175  		[enc endEncoding];
   176  	}
   177  }
   178  
   179  static void computeEncSetBuffer(CFTypeRef computeEncRef, NSUInteger index, CFTypeRef bufRef) {
   180  	@autoreleasepool {
   181  		id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
   182  		id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
   183  		[enc setBuffer:buf offset:0 atIndex:index];
   184  	}
   185  }
   186  
   187  static void computeEncDispatch(CFTypeRef computeEncRef, MTLSize threadgroupsPerGrid, MTLSize threadsPerThreadgroup) {
   188  	@autoreleasepool {
   189  		id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
   190  		[enc dispatchThreadgroups:threadgroupsPerGrid threadsPerThreadgroup:threadsPerThreadgroup];
   191  	}
   192  }
   193  
   194  static void computeEncSetBytes(CFTypeRef computeEncRef, const void *bytes, NSUInteger length, NSUInteger index) {
   195  	@autoreleasepool {
   196  		id<MTLComputeCommandEncoder> enc = (__bridge id<MTLComputeCommandEncoder>)computeEncRef;
   197  		[enc setBytes:bytes length:length atIndex:index];
   198  	}
   199  }
   200  
   201  static void blitEncEnd(CFTypeRef blitEncRef) {
   202  	@autoreleasepool {
   203  		id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
   204  		[enc endEncoding];
   205  	}
   206  }
   207  
   208  static void blitEncCopyFromTexture(CFTypeRef blitEncRef, CFTypeRef srcRef, MTLOrigin srcOrig, MTLSize srcSize, CFTypeRef dstRef, MTLOrigin dstOrig) {
   209  	@autoreleasepool {
   210  		id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
   211  		id<MTLTexture> src = (__bridge id<MTLTexture>)srcRef;
   212  		id<MTLTexture> dst = (__bridge id<MTLTexture>)dstRef;
   213  		[enc copyFromTexture:src
   214  				 sourceSlice:0
   215  				 sourceLevel:0
   216  			    sourceOrigin:srcOrig
   217  				  sourceSize:srcSize
   218  				   toTexture:dst
   219  			destinationSlice:0
   220  			destinationLevel:0
   221  		   destinationOrigin:dstOrig];
   222  	}
   223  }
   224  
   225  static void blitEncCopyBufferToTexture(CFTypeRef blitEncRef, CFTypeRef bufRef, CFTypeRef texRef, NSUInteger offset, NSUInteger stride, NSUInteger length, MTLSize dims, MTLOrigin orig) {
   226  	@autoreleasepool {
   227  		id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
   228  		id<MTLBuffer> src = (__bridge id<MTLBuffer>)bufRef;
   229  		id<MTLTexture> dst = (__bridge id<MTLTexture>)texRef;
   230  		[enc copyFromBuffer:src
   231  			   sourceOffset:offset
   232  		  sourceBytesPerRow:stride
   233  		sourceBytesPerImage:length
   234  				 sourceSize:dims
   235  				  toTexture:dst
   236  		   destinationSlice:0
   237  		   destinationLevel:0
   238  		  destinationOrigin:orig];
   239  	}
   240  }
   241  
   242  static void blitEncGenerateMipmapsForTexture(CFTypeRef blitEncRef, CFTypeRef texRef) {
   243  	@autoreleasepool {
   244  		id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
   245  		id<MTLTexture> tex = (__bridge id<MTLTexture>)texRef;
   246  		[enc generateMipmapsForTexture: tex];
   247  	}
   248  }
   249  
   250  static void blitEncCopyTextureToBuffer(CFTypeRef blitEncRef, CFTypeRef texRef, CFTypeRef bufRef, NSUInteger offset, NSUInteger stride, NSUInteger length, MTLSize dims, MTLOrigin orig) {
   251  	@autoreleasepool {
   252  		id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
   253  		id<MTLTexture> src = (__bridge id<MTLTexture>)texRef;
   254  		id<MTLBuffer> dst = (__bridge id<MTLBuffer>)bufRef;
   255  		[enc		 copyFromTexture:src
   256  						 sourceSlice:0
   257  						 sourceLevel:0
   258  						sourceOrigin:orig
   259  						  sourceSize:dims
   260  							toBuffer:dst
   261  				   destinationOffset:offset
   262  			  destinationBytesPerRow:stride
   263  			destinationBytesPerImage:length];
   264  	}
   265  }
   266  
   267  static void blitEncCopyBufferToBuffer(CFTypeRef blitEncRef, CFTypeRef srcRef, CFTypeRef dstRef, NSUInteger srcOff, NSUInteger dstOff, NSUInteger size) {
   268  	@autoreleasepool {
   269  		id<MTLBlitCommandEncoder> enc = (__bridge id<MTLBlitCommandEncoder>)blitEncRef;
   270  		id<MTLBuffer> src = (__bridge id<MTLBuffer>)srcRef;
   271  		id<MTLBuffer> dst = (__bridge id<MTLBuffer>)dstRef;
   272  		[enc   copyFromBuffer:src
   273  				 sourceOffset:srcOff
   274  					 toBuffer:dst
   275  			destinationOffset:dstOff
   276  						 size:size];
   277  	}
   278  }
   279  
   280  static CFTypeRef newTexture(CFTypeRef devRef, NSUInteger width, NSUInteger height, MTLPixelFormat format, MTLTextureUsage usage, int mipmapped) {
   281  	@autoreleasepool {
   282  		id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
   283  		MTLTextureDescriptor *mtlDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat: format
   284  																						   width: width
   285  																						  height: height
   286  																				   	   mipmapped: mipmapped ? YES : NO];
   287  		mtlDesc.usage = usage;
   288  		mtlDesc.storageMode =  MTLStorageModePrivate;
   289  		return CFBridgingRetain([dev newTextureWithDescriptor:mtlDesc]);
   290  	}
   291  }
   292  
   293  static CFTypeRef newSampler(CFTypeRef devRef, MTLSamplerMinMagFilter minFilter, MTLSamplerMinMagFilter magFilter, MTLSamplerMipFilter mipFilter) {
   294  	@autoreleasepool {
   295  		id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
   296  		MTLSamplerDescriptor *desc = [MTLSamplerDescriptor new];
   297  		desc.minFilter = minFilter;
   298  		desc.magFilter = magFilter;
   299  		desc.mipFilter = mipFilter;
   300  		return CFBridgingRetain([dev newSamplerStateWithDescriptor:desc]);
   301  	}
   302  }
   303  
   304  static CFTypeRef newBuffer(CFTypeRef devRef, NSUInteger size, MTLResourceOptions opts) {
   305  	@autoreleasepool {
   306  		id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
   307  		id<MTLBuffer> buf = [dev newBufferWithLength:size
   308  											 options:opts];
   309  		return CFBridgingRetain(buf);
   310  	}
   311  }
   312  
   313  static slice bufferContents(CFTypeRef bufRef) {
   314  	@autoreleasepool {
   315  		id<MTLBuffer> buf = (__bridge id<MTLBuffer>)bufRef;
   316  		slice s = {.addr = [buf contents], .size = [buf length]};
   317  		return s;
   318  	}
   319  }
   320  
   321  static CFTypeRef newLibrary(CFTypeRef devRef, char *name, void *mtllib, size_t size) {
   322  	@autoreleasepool {
   323  		id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
   324  		dispatch_data_t data = dispatch_data_create(mtllib, size, DISPATCH_TARGET_QUEUE_DEFAULT, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
   325  		id<MTLLibrary> lib = [dev newLibraryWithData:data error:nil];
   326  		lib.label = [NSString stringWithUTF8String:name];
   327  		return CFBridgingRetain(lib);
   328  	}
   329  }
   330  
   331  static CFTypeRef libraryNewFunction(CFTypeRef libRef, char *funcName) {
   332  	@autoreleasepool {
   333  		id<MTLLibrary> lib = (__bridge id<MTLLibrary>)libRef;
   334  		NSString *name = [NSString stringWithUTF8String:funcName];
   335  		return CFBridgingRetain([lib newFunctionWithName:name]);
   336  	}
   337  }
   338  
   339  static CFTypeRef newComputePipeline(CFTypeRef devRef, CFTypeRef funcRef) {
   340  	@autoreleasepool {
   341  		id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
   342  		id<MTLFunction> func = (__bridge id<MTLFunction>)funcRef;
   343  		return CFBridgingRetain([dev newComputePipelineStateWithFunction:func error:nil]);
   344  	}
   345  }
   346  
   347  static CFTypeRef newRenderPipeline(CFTypeRef devRef, CFTypeRef vertFunc, CFTypeRef fragFunc, MTLPixelFormat pixelFormat, NSUInteger bufIdx, NSUInteger nverts, MTLVertexFormat *fmts, NSUInteger *offsets, NSUInteger stride, int blend, MTLBlendFactor srcFactor, MTLBlendFactor dstFactor, NSUInteger nvertBufs, NSUInteger nfragBufs) {
   348  	@autoreleasepool {
   349  		id<MTLDevice> dev = (__bridge id<MTLDevice>)devRef;
   350  		id<MTLFunction> vfunc = (__bridge id<MTLFunction>)vertFunc;
   351  		id<MTLFunction> ffunc = (__bridge id<MTLFunction>)fragFunc;
   352  		MTLVertexDescriptor *vdesc = [MTLVertexDescriptor vertexDescriptor];
   353  		vdesc.layouts[bufIdx].stride = stride;
   354  		for (NSUInteger i = 0; i < nverts; i++) {
   355  			vdesc.attributes[i].format = fmts[i];
   356  			vdesc.attributes[i].offset = offsets[i];
   357  			vdesc.attributes[i].bufferIndex = bufIdx;
   358  		}
   359  		MTLRenderPipelineDescriptor *desc = [MTLRenderPipelineDescriptor new];
   360  		desc.vertexFunction = vfunc;
   361  		desc.fragmentFunction = ffunc;
   362  		desc.vertexDescriptor = vdesc;
   363  		for (NSUInteger i = 0; i < nvertBufs; i++) {
   364  			if (@available(iOS 11.0, *)) {
   365  				desc.vertexBuffers[i].mutability = MTLMutabilityImmutable;
   366  			}
   367  		}
   368  		for (NSUInteger i = 0; i < nfragBufs; i++) {
   369  			if (@available(iOS 11.0, *)) {
   370  				desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable;
   371  			}
   372  		}
   373  		desc.colorAttachments[0].pixelFormat = pixelFormat;
   374  		desc.colorAttachments[0].blendingEnabled = blend ? YES : NO;
   375  		desc.colorAttachments[0].sourceAlphaBlendFactor = srcFactor;
   376  		desc.colorAttachments[0].sourceRGBBlendFactor = srcFactor;
   377  		desc.colorAttachments[0].destinationAlphaBlendFactor = dstFactor;
   378  		desc.colorAttachments[0].destinationRGBBlendFactor = dstFactor;
   379  		return CFBridgingRetain([dev newRenderPipelineStateWithDescriptor:desc
   380  																	error:nil]);
   381  	}
   382  }
   383  */
   384  import "C"
   385  
   386  type Backend struct {
   387  	dev      C.CFTypeRef
   388  	queue    C.CFTypeRef
   389  	pixelFmt C.MTLPixelFormat
   390  
   391  	cmdBuffer     C.CFTypeRef
   392  	lastCmdBuffer C.CFTypeRef
   393  	renderEnc     C.CFTypeRef
   394  	computeEnc    C.CFTypeRef
   395  	blitEnc       C.CFTypeRef
   396  
   397  	prog     *Program
   398  	topology C.MTLPrimitiveType
   399  
   400  	stagingBuf C.CFTypeRef
   401  	stagingOff int
   402  
   403  	indexBuf *Buffer
   404  
   405  	// bufSizes is scratch space for filling out the spvBufferSizeConstants
   406  	// that spirv-cross generates for emulating buffer.length expressions in
   407  	// shaders.
   408  	bufSizes []uint32
   409  }
   410  
   411  type Texture struct {
   412  	backend *Backend
   413  	texture C.CFTypeRef
   414  	sampler C.CFTypeRef
   415  	width   int
   416  	height  int
   417  	mipmap  bool
   418  	foreign bool
   419  }
   420  
   421  type Shader struct {
   422  	function C.CFTypeRef
   423  	inputs   []shader.InputLocation
   424  }
   425  
   426  type Program struct {
   427  	pipeline  C.CFTypeRef
   428  	groupSize [3]int
   429  }
   430  
   431  type Pipeline struct {
   432  	pipeline C.CFTypeRef
   433  	topology C.MTLPrimitiveType
   434  }
   435  
   436  type Buffer struct {
   437  	backend *Backend
   438  	size    int
   439  	buffer  C.CFTypeRef
   440  
   441  	// store is the buffer contents For buffers not allocated on the GPU.
   442  	store []byte
   443  }
   444  
   445  const (
   446  	uniformBufferIndex   = 0
   447  	attributeBufferIndex = 1
   448  
   449  	spvBufferSizeConstantsBinding = 25
   450  )
   451  
   452  const (
   453  	texUnits    = 4
   454  	bufferUnits = 4
   455  )
   456  
   457  func init() {
   458  	driver.NewMetalDevice = newMetalDevice
   459  }
   460  
   461  func newMetalDevice(api driver.Metal) (driver.Device, error) {
   462  	dev := C.CFTypeRef(api.Device)
   463  	C.CFRetain(dev)
   464  	queue := C.CFTypeRef(api.Queue)
   465  	C.CFRetain(queue)
   466  	b := &Backend{
   467  		dev:      dev,
   468  		queue:    queue,
   469  		pixelFmt: C.MTLPixelFormat(api.PixelFormat),
   470  		bufSizes: make([]uint32, bufferUnits),
   471  	}
   472  	return b, nil
   473  }
   474  
   475  func (b *Backend) BeginFrame(target driver.RenderTarget, clear bool, viewport image.Point) driver.Texture {
   476  	if b.lastCmdBuffer != 0 {
   477  		C.cmdBufferWaitUntilCompleted(b.lastCmdBuffer)
   478  		b.stagingOff = 0
   479  	}
   480  	if target == nil {
   481  		return nil
   482  	}
   483  	switch t := target.(type) {
   484  	case driver.MetalRenderTarget:
   485  		texture := C.CFTypeRef(t.Texture)
   486  		return &Texture{texture: texture, foreign: true}
   487  	case *Texture:
   488  		return t
   489  	default:
   490  		panic(fmt.Sprintf("metal: unsupported render target type: %T", t))
   491  	}
   492  }
   493  
   494  func (b *Backend) startBlit() C.CFTypeRef {
   495  	if b.blitEnc != 0 {
   496  		return b.blitEnc
   497  	}
   498  	b.endEncoder()
   499  	b.ensureCmdBuffer()
   500  	b.blitEnc = C.cmdBufferBlitEncoder(b.cmdBuffer)
   501  	if b.blitEnc == 0 {
   502  		panic("metal: [MTLCommandBuffer blitCommandEncoder:] failed")
   503  	}
   504  	return b.blitEnc
   505  }
   506  
   507  func (b *Backend) CopyTexture(dst driver.Texture, dorig image.Point, src driver.Texture, srect image.Rectangle) {
   508  	enc := b.startBlit()
   509  	dstTex := dst.(*Texture).texture
   510  	srcTex := src.(*Texture).texture
   511  	ssz := srect.Size()
   512  	C.blitEncCopyFromTexture(
   513  		enc,
   514  		srcTex,
   515  		C.MTLOrigin{
   516  			x: C.NSUInteger(srect.Min.X),
   517  			y: C.NSUInteger(srect.Min.Y),
   518  		},
   519  		C.MTLSize{
   520  			width:  C.NSUInteger(ssz.X),
   521  			height: C.NSUInteger(ssz.Y),
   522  			depth:  1,
   523  		},
   524  		dstTex,
   525  		C.MTLOrigin{
   526  			x: C.NSUInteger(dorig.X),
   527  			y: C.NSUInteger(dorig.Y),
   528  		},
   529  	)
   530  }
   531  
   532  func (b *Backend) EndFrame() {
   533  	b.endCmdBuffer(false)
   534  }
   535  
   536  func (b *Backend) endCmdBuffer(wait bool) {
   537  	b.endEncoder()
   538  	if b.cmdBuffer == 0 {
   539  		return
   540  	}
   541  	C.cmdBufferCommit(b.cmdBuffer)
   542  	if wait {
   543  		C.cmdBufferWaitUntilCompleted(b.cmdBuffer)
   544  	}
   545  	if b.lastCmdBuffer != 0 {
   546  		C.CFRelease(b.lastCmdBuffer)
   547  	}
   548  	b.lastCmdBuffer = b.cmdBuffer
   549  	b.cmdBuffer = 0
   550  }
   551  
   552  func (b *Backend) Caps() driver.Caps {
   553  	return driver.Caps{
   554  		MaxTextureSize: 8192,
   555  		Features:       driver.FeatureSRGB | driver.FeatureCompute | driver.FeatureFloatRenderTargets,
   556  	}
   557  }
   558  
   559  func (b *Backend) NewTimer() driver.Timer {
   560  	panic("timers not supported")
   561  }
   562  
   563  func (b *Backend) IsTimeContinuous() bool {
   564  	panic("timers not supported")
   565  }
   566  
   567  func (b *Backend) Release() {
   568  	if b.cmdBuffer != 0 {
   569  		C.CFRelease(b.cmdBuffer)
   570  	}
   571  	if b.lastCmdBuffer != 0 {
   572  		C.CFRelease(b.lastCmdBuffer)
   573  	}
   574  	if b.stagingBuf != 0 {
   575  		C.CFRelease(b.stagingBuf)
   576  	}
   577  	C.CFRelease(b.queue)
   578  	C.CFRelease(b.dev)
   579  	*b = Backend{}
   580  }
   581  
   582  func (b *Backend) NewTexture(format driver.TextureFormat, width, height int, minFilter, magFilter driver.TextureFilter, bindings driver.BufferBinding) (driver.Texture, error) {
   583  	mformat := pixelFormatFor(format)
   584  	var usage C.MTLTextureUsage
   585  	if bindings&(driver.BufferBindingTexture|driver.BufferBindingShaderStorageRead) != 0 {
   586  		usage |= C.MTLTextureUsageShaderRead
   587  	}
   588  	if bindings&driver.BufferBindingFramebuffer != 0 {
   589  		usage |= C.MTLTextureUsageRenderTarget
   590  	}
   591  	if bindings&driver.BufferBindingShaderStorageWrite != 0 {
   592  		usage |= C.MTLTextureUsageShaderWrite
   593  	}
   594  	min, mip := samplerFilterFor(minFilter)
   595  	max, _ := samplerFilterFor(magFilter)
   596  	mipmap := mip != C.MTLSamplerMipFilterNotMipmapped
   597  	mipmapped := C.int(0)
   598  	if mipmap {
   599  		mipmapped = 1
   600  	}
   601  	tex := C.newTexture(b.dev, C.NSUInteger(width), C.NSUInteger(height), mformat, usage, mipmapped)
   602  	if tex == 0 {
   603  		return nil, errors.New("metal: [MTLDevice newTextureWithDescriptor:] failed")
   604  	}
   605  	s := C.newSampler(b.dev, min, max, mip)
   606  	if s == 0 {
   607  		C.CFRelease(tex)
   608  		return nil, errors.New("metal: [MTLDevice newSamplerStateWithDescriptor:] failed")
   609  	}
   610  	return &Texture{backend: b, texture: tex, sampler: s, width: width, height: height, mipmap: mipmap}, nil
   611  }
   612  
   613  func samplerFilterFor(f driver.TextureFilter) (C.MTLSamplerMinMagFilter, C.MTLSamplerMipFilter) {
   614  	switch f {
   615  	case driver.FilterNearest:
   616  		return C.MTLSamplerMinMagFilterNearest, C.MTLSamplerMipFilterNotMipmapped
   617  	case driver.FilterLinear:
   618  		return C.MTLSamplerMinMagFilterLinear, C.MTLSamplerMipFilterNotMipmapped
   619  	case driver.FilterLinearMipmapLinear:
   620  		return C.MTLSamplerMinMagFilterLinear, C.MTLSamplerMipFilterLinear
   621  	default:
   622  		panic("invalid texture filter")
   623  	}
   624  }
   625  
   626  func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
   627  	vsh, fsh := desc.VertexShader.(*Shader), desc.FragmentShader.(*Shader)
   628  	layout := desc.VertexLayout.Inputs
   629  	if got, exp := len(layout), len(vsh.inputs); got != exp {
   630  		return nil, fmt.Errorf("metal: number of input descriptors (%d) doesn't match number of inputs (%d)", got, exp)
   631  	}
   632  	formats := make([]C.MTLVertexFormat, len(layout))
   633  	offsets := make([]C.NSUInteger, len(layout))
   634  	for i, inp := range layout {
   635  		index := vsh.inputs[i].Location
   636  		formats[index] = vertFormatFor(vsh.inputs[i])
   637  		offsets[index] = C.NSUInteger(inp.Offset)
   638  	}
   639  	var (
   640  		fmtPtr *C.MTLVertexFormat
   641  		offPtr *C.NSUInteger
   642  	)
   643  	if len(layout) > 0 {
   644  		fmtPtr = &formats[0]
   645  		offPtr = &offsets[0]
   646  	}
   647  	srcFactor := blendFactorFor(desc.BlendDesc.SrcFactor)
   648  	dstFactor := blendFactorFor(desc.BlendDesc.DstFactor)
   649  	blend := C.int(0)
   650  	if desc.BlendDesc.Enable {
   651  		blend = 1
   652  	}
   653  	pf := b.pixelFmt
   654  	if f := desc.PixelFormat; f != driver.TextureFormatOutput {
   655  		pf = pixelFormatFor(f)
   656  	}
   657  	pipe := C.newRenderPipeline(
   658  		b.dev,
   659  		vsh.function,
   660  		fsh.function,
   661  		pf,
   662  		attributeBufferIndex,
   663  		C.NSUInteger(len(layout)), fmtPtr, offPtr,
   664  		C.NSUInteger(desc.VertexLayout.Stride),
   665  		blend, srcFactor, dstFactor,
   666  		2, // Number of vertex buffers.
   667  		1, // Number of fragment buffers.
   668  	)
   669  	if pipe == 0 {
   670  		return nil, errors.New("metal: pipeline construction failed")
   671  	}
   672  	return &Pipeline{pipeline: pipe, topology: primitiveFor(desc.Topology)}, nil
   673  }
   674  
   675  func dataTypeSize(d shader.DataType) int {
   676  	switch d {
   677  	case shader.DataTypeFloat:
   678  		return 4
   679  	default:
   680  		panic("unsupported data type")
   681  	}
   682  }
   683  
   684  func blendFactorFor(f driver.BlendFactor) C.MTLBlendFactor {
   685  	switch f {
   686  	case driver.BlendFactorZero:
   687  		return C.MTLBlendFactorZero
   688  	case driver.BlendFactorOne:
   689  		return C.MTLBlendFactorOne
   690  	case driver.BlendFactorOneMinusSrcAlpha:
   691  		return C.MTLBlendFactorOneMinusSourceAlpha
   692  	case driver.BlendFactorDstColor:
   693  		return C.MTLBlendFactorDestinationColor
   694  	default:
   695  		panic("unsupported blend factor")
   696  	}
   697  }
   698  
   699  func vertFormatFor(f shader.InputLocation) C.MTLVertexFormat {
   700  	t := f.Type
   701  	s := f.Size
   702  	switch {
   703  	case t == shader.DataTypeFloat && s == 1:
   704  		return C.MTLVertexFormatFloat
   705  	case t == shader.DataTypeFloat && s == 2:
   706  		return C.MTLVertexFormatFloat2
   707  	case t == shader.DataTypeFloat && s == 3:
   708  		return C.MTLVertexFormatFloat3
   709  	case t == shader.DataTypeFloat && s == 4:
   710  		return C.MTLVertexFormatFloat4
   711  	default:
   712  		panic("unsupported data type")
   713  	}
   714  }
   715  
   716  func pixelFormatFor(f driver.TextureFormat) C.MTLPixelFormat {
   717  	switch f {
   718  	case driver.TextureFormatFloat:
   719  		return C.MTLPixelFormatR16Float
   720  	case driver.TextureFormatRGBA8:
   721  		return C.MTLPixelFormatRGBA8Unorm
   722  	case driver.TextureFormatSRGBA:
   723  		return C.MTLPixelFormatRGBA8Unorm_sRGB
   724  	default:
   725  		panic("unsupported pixel format")
   726  	}
   727  }
   728  
   729  func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
   730  	// Transfer buffer contents in command encoders on every use for
   731  	// smaller buffers. The advantage is that buffer re-use during a frame
   732  	// won't occur a GPU wait.
   733  	// We can't do this for buffers written to by the GPU and read by the client,
   734  	// and Metal doesn't require a buffer for indexed draws.
   735  	if size <= 4096 && typ&(driver.BufferBindingShaderStorageWrite|driver.BufferBindingIndices) == 0 {
   736  		return &Buffer{size: size, store: make([]byte, size)}, nil
   737  	}
   738  	buf := C.newBuffer(b.dev, C.NSUInteger(size), C.MTLResourceStorageModePrivate)
   739  	return &Buffer{backend: b, size: size, buffer: buf}, nil
   740  }
   741  
   742  func (b *Backend) NewImmutableBuffer(typ driver.BufferBinding, data []byte) (driver.Buffer, error) {
   743  	buf, err := b.NewBuffer(typ, len(data))
   744  	if err != nil {
   745  		return nil, err
   746  	}
   747  	buf.Upload(data)
   748  	return buf, nil
   749  }
   750  
   751  func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) {
   752  	sh, err := b.newShader(src)
   753  	if err != nil {
   754  		return nil, err
   755  	}
   756  	defer sh.Release()
   757  	pipe := C.newComputePipeline(b.dev, sh.function)
   758  	if pipe == 0 {
   759  		return nil, fmt.Errorf("metal: compute program %q load failed", src.Name)
   760  	}
   761  	return &Program{pipeline: pipe, groupSize: src.WorkgroupSize}, nil
   762  }
   763  
   764  func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
   765  	return b.newShader(src)
   766  }
   767  
   768  func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
   769  	return b.newShader(src)
   770  }
   771  
   772  func (b *Backend) newShader(src shader.Sources) (*Shader, error) {
   773  	vsrc := []byte(src.MetalLib)
   774  	cname := C.CString(src.Name)
   775  	defer C.free(unsafe.Pointer(cname))
   776  	vlib := C.newLibrary(b.dev, cname, unsafe.Pointer(&vsrc[0]), C.size_t(len(vsrc)))
   777  	if vlib == 0 {
   778  		return nil, fmt.Errorf("metal: vertex shader %q load failed", src.Name)
   779  	}
   780  	defer C.CFRelease(vlib)
   781  	funcName := C.CString("main0")
   782  	defer C.free(unsafe.Pointer(funcName))
   783  	f := C.libraryNewFunction(vlib, funcName)
   784  	if f == 0 {
   785  		return nil, fmt.Errorf("metal: main function not found in %q", src.Name)
   786  	}
   787  	return &Shader{function: f, inputs: src.Inputs}, nil
   788  }
   789  
   790  func (b *Backend) Viewport(x, y, width, height int) {
   791  	enc := b.renderEnc
   792  	if enc == 0 {
   793  		panic("no active render pass")
   794  	}
   795  	C.renderEncViewport(enc, C.MTLViewport{
   796  		originX: C.double(x),
   797  		originY: C.double(y),
   798  		width:   C.double(width),
   799  		height:  C.double(height),
   800  		znear:   0.0,
   801  		zfar:    1.0,
   802  	})
   803  }
   804  
   805  func (b *Backend) DrawArrays(off, count int) {
   806  	enc := b.renderEnc
   807  	if enc == 0 {
   808  		panic("no active render pass")
   809  	}
   810  	C.renderEncDrawPrimitives(enc, b.topology, C.NSUInteger(off), C.NSUInteger(count))
   811  }
   812  
   813  func (b *Backend) DrawElements(off, count int) {
   814  	enc := b.renderEnc
   815  	if enc == 0 {
   816  		panic("no active render pass")
   817  	}
   818  	C.renderEncDrawIndexedPrimitives(enc, b.topology, b.indexBuf.buffer, C.NSUInteger(off), C.NSUInteger(count))
   819  }
   820  
   821  func primitiveFor(mode driver.Topology) C.MTLPrimitiveType {
   822  	switch mode {
   823  	case driver.TopologyTriangles:
   824  		return C.MTLPrimitiveTypeTriangle
   825  	case driver.TopologyTriangleStrip:
   826  		return C.MTLPrimitiveTypeTriangleStrip
   827  	default:
   828  		panic("metal: unknown draw mode")
   829  	}
   830  }
   831  
   832  func (b *Backend) BindImageTexture(unit int, tex driver.Texture) {
   833  	b.BindTexture(unit, tex)
   834  }
   835  
   836  func (b *Backend) BeginCompute() {
   837  	b.endEncoder()
   838  	b.ensureCmdBuffer()
   839  	for i := range b.bufSizes {
   840  		b.bufSizes[i] = 0
   841  	}
   842  	b.computeEnc = C.cmdBufferComputeEncoder(b.cmdBuffer)
   843  	if b.computeEnc == 0 {
   844  		panic("metal: [MTLCommandBuffer computeCommandEncoder:] failed")
   845  	}
   846  }
   847  
   848  func (b *Backend) EndCompute() {
   849  	if b.computeEnc == 0 {
   850  		panic("no active compute pass")
   851  	}
   852  	C.computeEncEnd(b.computeEnc)
   853  	C.CFRelease(b.computeEnc)
   854  	b.computeEnc = 0
   855  }
   856  
   857  func (b *Backend) DispatchCompute(x, y, z int) {
   858  	enc := b.computeEnc
   859  	if enc == 0 {
   860  		panic("no active compute pass")
   861  	}
   862  	C.computeEncSetBytes(enc, unsafe.Pointer(&b.bufSizes[0]), C.NSUInteger(len(b.bufSizes)*4), spvBufferSizeConstantsBinding)
   863  	threadgroupsPerGrid := C.MTLSize{
   864  		width: C.NSUInteger(x), height: C.NSUInteger(y), depth: C.NSUInteger(z),
   865  	}
   866  	sz := b.prog.groupSize
   867  	threadsPerThreadgroup := C.MTLSize{
   868  		width: C.NSUInteger(sz[0]), height: C.NSUInteger(sz[1]), depth: C.NSUInteger(sz[2]),
   869  	}
   870  	C.computeEncDispatch(enc, threadgroupsPerGrid, threadsPerThreadgroup)
   871  }
   872  
   873  func (b *Backend) stagingBuffer(size int) (C.CFTypeRef, int) {
   874  	if b.stagingBuf == 0 || b.stagingOff+size > len(bufferStore(b.stagingBuf)) {
   875  		if b.stagingBuf != 0 {
   876  			C.CFRelease(b.stagingBuf)
   877  		}
   878  		cap := 2 * (b.stagingOff + size)
   879  		b.stagingBuf = C.newBuffer(b.dev, C.NSUInteger(cap), C.MTLResourceStorageModeShared|C.MTLResourceCPUCacheModeWriteCombined)
   880  		if b.stagingBuf == 0 {
   881  			panic(fmt.Errorf("metal: failed to allocate %d bytes of staging buffer", cap))
   882  		}
   883  		b.stagingOff = 0
   884  	}
   885  	off := b.stagingOff
   886  	b.stagingOff += size
   887  	return b.stagingBuf, off
   888  }
   889  
   890  func (t *Texture) Upload(offset, size image.Point, pixels []byte, stride int) {
   891  	if len(pixels) == 0 {
   892  		return
   893  	}
   894  	if stride == 0 {
   895  		stride = size.X * 4
   896  	}
   897  	dstStride := size.X * 4
   898  	n := size.Y * dstStride
   899  	buf, off := t.backend.stagingBuffer(n)
   900  	store := bufferSlice(buf, off, n)
   901  	var srcOff, dstOff int
   902  	for y := 0; y < size.Y; y++ {
   903  		srcRow := pixels[srcOff : srcOff+dstStride]
   904  		dstRow := store[dstOff : dstOff+dstStride]
   905  		copy(dstRow, srcRow)
   906  		dstOff += dstStride
   907  		srcOff += stride
   908  	}
   909  	enc := t.backend.startBlit()
   910  	orig := C.MTLOrigin{
   911  		x: C.NSUInteger(offset.X),
   912  		y: C.NSUInteger(offset.Y),
   913  	}
   914  	msize := C.MTLSize{
   915  		width:  C.NSUInteger(size.X),
   916  		height: C.NSUInteger(size.Y),
   917  		depth:  1,
   918  	}
   919  	C.blitEncCopyBufferToTexture(enc, buf, t.texture, C.NSUInteger(off), C.NSUInteger(dstStride), C.NSUInteger(len(store)), msize, orig)
   920  	if t.mipmap {
   921  		C.blitEncGenerateMipmapsForTexture(enc, t.texture)
   922  	}
   923  }
   924  
   925  func (t *Texture) Release() {
   926  	if t.foreign {
   927  		panic("metal: release of external texture")
   928  	}
   929  	C.CFRelease(t.texture)
   930  	C.CFRelease(t.sampler)
   931  	*t = Texture{}
   932  }
   933  
   934  func (p *Pipeline) Release() {
   935  	C.CFRelease(p.pipeline)
   936  	*p = Pipeline{}
   937  }
   938  
   939  func (b *Backend) PrepareTexture(tex driver.Texture) {}
   940  
   941  func (b *Backend) BindTexture(unit int, tex driver.Texture) {
   942  	t := tex.(*Texture)
   943  	if enc := b.renderEnc; enc != 0 {
   944  		C.renderEncSetFragmentTexture(enc, C.NSUInteger(unit), t.texture)
   945  		C.renderEncSetFragmentSamplerState(enc, C.NSUInteger(unit), t.sampler)
   946  	} else if enc := b.computeEnc; enc != 0 {
   947  		C.computeEncSetTexture(enc, C.NSUInteger(unit), t.texture)
   948  	} else {
   949  		panic("no active render nor compute pass")
   950  	}
   951  }
   952  
   953  func (b *Backend) ensureCmdBuffer() {
   954  	if b.cmdBuffer != 0 {
   955  		return
   956  	}
   957  	b.cmdBuffer = C.queueNewBuffer(b.queue)
   958  	if b.cmdBuffer == 0 {
   959  		panic("metal: [MTLCommandQueue cmdBuffer] failed")
   960  	}
   961  }
   962  
   963  func (b *Backend) BindPipeline(pipe driver.Pipeline) {
   964  	p := pipe.(*Pipeline)
   965  	enc := b.renderEnc
   966  	if enc == 0 {
   967  		panic("no active render pass")
   968  	}
   969  	C.renderEncSetRenderPipelineState(enc, p.pipeline)
   970  	b.topology = p.topology
   971  }
   972  
   973  func (b *Backend) BindProgram(prog driver.Program) {
   974  	enc := b.computeEnc
   975  	if enc == 0 {
   976  		panic("no active compute pass")
   977  	}
   978  	p := prog.(*Program)
   979  	C.computeEncSetPipeline(enc, p.pipeline)
   980  	b.prog = p
   981  }
   982  
   983  func (s *Shader) Release() {
   984  	C.CFRelease(s.function)
   985  	*s = Shader{}
   986  }
   987  
   988  func (p *Program) Release() {
   989  	C.CFRelease(p.pipeline)
   990  	*p = Program{}
   991  }
   992  
   993  func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) {
   994  	buf := buffer.(*Buffer)
   995  	b.bufSizes[binding] = uint32(buf.size)
   996  	enc := b.computeEnc
   997  	if enc == 0 {
   998  		panic("no active compute pass")
   999  	}
  1000  	if buf.buffer != 0 {
  1001  		C.computeEncSetBuffer(enc, C.NSUInteger(binding), buf.buffer)
  1002  	} else if buf.size > 0 {
  1003  		C.computeEncSetBytes(enc, unsafe.Pointer(&buf.store[0]), C.NSUInteger(buf.size), C.NSUInteger(binding))
  1004  	}
  1005  }
  1006  
  1007  func (b *Backend) BindUniforms(buf driver.Buffer) {
  1008  	bf := buf.(*Buffer)
  1009  	enc := b.renderEnc
  1010  	if enc == 0 {
  1011  		panic("no active render pass")
  1012  	}
  1013  	if bf.buffer != 0 {
  1014  		C.renderEncSetVertexBuffer(enc, bf.buffer, uniformBufferIndex, 0)
  1015  		C.renderEncSetFragmentBuffer(enc, bf.buffer, uniformBufferIndex, 0)
  1016  	} else if bf.size > 0 {
  1017  		C.renderEncSetVertexBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex)
  1018  		C.renderEncSetFragmentBytes(enc, unsafe.Pointer(&bf.store[0]), C.NSUInteger(bf.size), uniformBufferIndex)
  1019  	}
  1020  }
  1021  
  1022  func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) {
  1023  	bf := buf.(*Buffer)
  1024  	enc := b.renderEnc
  1025  	if enc == 0 {
  1026  		panic("no active render pass")
  1027  	}
  1028  	if bf.buffer != 0 {
  1029  		C.renderEncSetVertexBuffer(enc, bf.buffer, attributeBufferIndex, C.NSUInteger(offset))
  1030  	} else if n := bf.size - offset; n > 0 {
  1031  		C.renderEncSetVertexBytes(enc, unsafe.Pointer(&bf.store[offset]), C.NSUInteger(n), attributeBufferIndex)
  1032  	}
  1033  }
  1034  
  1035  func (b *Backend) BindIndexBuffer(buf driver.Buffer) {
  1036  	b.indexBuf = buf.(*Buffer)
  1037  }
  1038  
  1039  func (b *Buffer) Download(data []byte) error {
  1040  	if len(data) > b.size {
  1041  		panic(fmt.Errorf("len(data) (%d) larger than len(content) (%d)", len(data), b.size))
  1042  	}
  1043  	buf, off := b.backend.stagingBuffer(len(data))
  1044  	enc := b.backend.startBlit()
  1045  	C.blitEncCopyBufferToBuffer(enc, b.buffer, buf, 0, C.NSUInteger(off), C.NSUInteger(len(data)))
  1046  	b.backend.endCmdBuffer(true)
  1047  	store := bufferSlice(buf, off, len(data))
  1048  	copy(data, store)
  1049  	return nil
  1050  }
  1051  
  1052  func (b *Buffer) Upload(data []byte) {
  1053  	if len(data) > b.size {
  1054  		panic(fmt.Errorf("len(data) (%d) larger than len(content) (%d)", len(data), b.size))
  1055  	}
  1056  	if b.buffer == 0 {
  1057  		copy(b.store, data)
  1058  		return
  1059  	}
  1060  	buf, off := b.backend.stagingBuffer(len(data))
  1061  	store := bufferSlice(buf, off, len(data))
  1062  	copy(store, data)
  1063  	enc := b.backend.startBlit()
  1064  	C.blitEncCopyBufferToBuffer(enc, buf, b.buffer, C.NSUInteger(off), 0, C.NSUInteger(len(store)))
  1065  }
  1066  
  1067  func bufferStore(buf C.CFTypeRef) []byte {
  1068  	contents := C.bufferContents(buf)
  1069  	return (*(*[1 << 30]byte)(contents.addr))[:contents.size:contents.size]
  1070  }
  1071  
  1072  func bufferSlice(buf C.CFTypeRef, off, len int) []byte {
  1073  	store := bufferStore(buf)
  1074  	return store[off : off+len]
  1075  }
  1076  
  1077  func (b *Buffer) Release() {
  1078  	if b.buffer != 0 {
  1079  		C.CFRelease(b.buffer)
  1080  	}
  1081  	*b = Buffer{}
  1082  }
  1083  
  1084  func (t *Texture) ReadPixels(src image.Rectangle, pixels []byte, stride int) error {
  1085  	if len(pixels) == 0 {
  1086  		return nil
  1087  	}
  1088  	sz := src.Size()
  1089  	orig := C.MTLOrigin{
  1090  		x: C.NSUInteger(src.Min.X),
  1091  		y: C.NSUInteger(src.Min.Y),
  1092  	}
  1093  	msize := C.MTLSize{
  1094  		width:  C.NSUInteger(sz.X),
  1095  		height: C.NSUInteger(sz.Y),
  1096  		depth:  1,
  1097  	}
  1098  	stageStride := sz.X * 4
  1099  	n := sz.Y * stageStride
  1100  	buf, off := t.backend.stagingBuffer(n)
  1101  	enc := t.backend.startBlit()
  1102  	C.blitEncCopyTextureToBuffer(enc, t.texture, buf, C.NSUInteger(off), C.NSUInteger(stageStride), C.NSUInteger(n), msize, orig)
  1103  	t.backend.endCmdBuffer(true)
  1104  	store := bufferSlice(buf, off, n)
  1105  	var srcOff, dstOff int
  1106  	for y := 0; y < sz.Y; y++ {
  1107  		dstRow := pixels[srcOff : srcOff+stageStride]
  1108  		srcRow := store[dstOff : dstOff+stageStride]
  1109  		copy(dstRow, srcRow)
  1110  		dstOff += stageStride
  1111  		srcOff += stride
  1112  	}
  1113  	return nil
  1114  }
  1115  
  1116  func (b *Backend) BeginRenderPass(tex driver.Texture, d driver.LoadDesc) {
  1117  	b.endEncoder()
  1118  	b.ensureCmdBuffer()
  1119  	f := tex.(*Texture)
  1120  	col := d.ClearColor
  1121  	var act C.MTLLoadAction
  1122  	switch d.Action {
  1123  	case driver.LoadActionKeep:
  1124  		act = C.MTLLoadActionLoad
  1125  	case driver.LoadActionClear:
  1126  		act = C.MTLLoadActionClear
  1127  	case driver.LoadActionInvalidate:
  1128  		act = C.MTLLoadActionDontCare
  1129  	}
  1130  	b.renderEnc = C.cmdBufferRenderEncoder(b.cmdBuffer, f.texture, act, C.float(col.R), C.float(col.G), C.float(col.B), C.float(col.A))
  1131  	if b.renderEnc == 0 {
  1132  		panic("metal: [MTLCommandBuffer renderCommandEncoderWithDescriptor:] failed")
  1133  	}
  1134  }
  1135  
  1136  func (b *Backend) EndRenderPass() {
  1137  	if b.renderEnc == 0 {
  1138  		panic("no active render pass")
  1139  	}
  1140  	C.renderEncEnd(b.renderEnc)
  1141  	C.CFRelease(b.renderEnc)
  1142  	b.renderEnc = 0
  1143  }
  1144  
  1145  func (b *Backend) endEncoder() {
  1146  	if b.renderEnc != 0 {
  1147  		panic("active render pass")
  1148  	}
  1149  	if b.computeEnc != 0 {
  1150  		panic("active compute pass")
  1151  	}
  1152  	if b.blitEnc != 0 {
  1153  		C.blitEncEnd(b.blitEnc)
  1154  		C.CFRelease(b.blitEnc)
  1155  		b.blitEnc = 0
  1156  	}
  1157  }
  1158  
  1159  func (f *Texture) ImplementsRenderTarget() {}