/ fdf / fdf / mlx / mlx_window.swift
mlx_window.swift
  1  
  2  import Cocoa
  3  import Metal
  4  import MetalKit
  5  import Darwin
  6  
  7  import mlx_image
  8  
  9  
 10  class WinEvent: NSWindow
 11  {
 12    var eventFuncts = [UnsafeMutableRawPointer?]()
 13    var eventParams = [UnsafeMutableRawPointer]()
 14  ///  var eventParams = UnsafeMutableRawPointer.allocate(capacity: 32)
 15  
 16    var keyrepeat = 1
 17    var keyflag:UInt32 = 0
 18  
 19    public var size_y:Int
 20  
 21    init(frame rect:CGRect)
 22    {
 23  ///      eventParams.initialize(to:nil, count:32)
 24     var ptr = UnsafeMutableRawPointer(bitPattern:1)!
 25     ptr -= 1
 26     for _ in 0...31
 27      {
 28        eventFuncts.append(Optional.none)
 29        /// eventParams.append(UnsafeMutableRawPointer(&keyrepeat)) /// dummy address here, null not needed
 30        eventParams.append(ptr)
 31      }
 32  
 33      let wsm = NSWindow.StyleMask(rawValue: NSWindow.StyleMask.titled.rawValue|NSWindow.StyleMask.closable.rawValue|NSWindow.StyleMask.miniaturizable.rawValue)
 34      let bck = NSWindow.BackingStoreType.buffered
 35      size_y = Int(rect.size.height)
 36      super.init(contentRect: rect, styleMask: wsm, backing: bck, defer: false)
 37    }
 38  
 39    func setNotifs()
 40    {
 41        NotificationCenter.default.addObserver(self, selector: #selector(exposeNotification(_:)), name: NSWindow.didBecomeKeyNotification, object: nil)
 42        NotificationCenter.default.addObserver(self, selector: #selector(deminiaturizeNotification(_:)), name: NSWindow.didDeminiaturizeNotification, object: nil)
 43        NotificationCenter.default.addObserver(self, selector: #selector(closeNotification(_:)), name: NSWindow.willCloseNotification, object: nil)
 44  
 45  /***
 46        [[NSNotificationCenter defaultCenter] addObserver:win selector:@selector(exposeNotification:) name:@"NSWindowDidBecomeKeyNotification" object:win];
 47        [[NSNotificationCenter defaultCenter] addObserver:win selector:@selector(deminiaturizeNotification:) name:@"NSWindowDidDeminiaturizeNotification" object:win];
 48        [[NSNotificationCenter defaultCenter] addObserver:win selector:@selector(closeNotification:) name:@"NSWindowWillCloseNotification" object:win];
 49  ***/
 50  
 51    }
 52  
 53    func delNotifs()
 54    {
 55        NotificationCenter.default.removeObserver(self, name: NSWindow.willCloseNotification, object: nil)
 56    }
 57  
 58    public func setKeyRepeat(_ mode:Int)
 59    {
 60  	keyrepeat = mode;
 61    }
 62  
 63  
 64    func addHook(index idx:Int, fct fptr:UnsafeMutableRawPointer?, param pptr:UnsafeMutableRawPointer)
 65    {
 66  	eventFuncts[idx] = fptr;
 67  	eventParams[idx] = pptr;
 68  	if (idx == 6 || idx == 32)
 69  	{
 70  		if (fptr != nil) ///  == nullptr)
 71  		   { self.acceptsMouseMovedEvents = true }
 72  		else { self.acceptsMouseMovedEvents = false }
 73  	}
 74    }
 75  
 76  
 77    override func keyDown(with event: NSEvent)
 78    {
 79  	/// print("got keydown with code: \(event.keyCode) ")
 80  	if (event.isARepeat && keyrepeat == 0)
 81  	 { return }
 82  	if (eventFuncts[2] != nil)
 83  	{
 84  	  _ = unsafeBitCast(eventFuncts[2],to:(@convention(c)(Int32, UnsafeRawPointer)->Int32).self)(Int32(event.keyCode), eventParams[2])
 85  	}
 86    }
 87  
 88  
 89    override func keyUp(with event: NSEvent)
 90    {
 91  	/// print("got keyup with code: \(event.keyCode) and calling key hook")
 92  	if (event.isARepeat && keyrepeat == 0)
 93  	 { return }
 94  	if (eventFuncts[3] != nil)
 95  	{
 96  	  _ = unsafeBitCast(eventFuncts[3],to:(@convention(c)(Int32, UnsafeRawPointer)->Int32).self)(Int32(event.keyCode), eventParams[3])
 97  	}
 98    }
 99  
100  
101    func get_mouse_button(with ev:NSEvent) -> Int
102    {
103  	switch (ev.type) {
104    	       case NSEvent.EventType.leftMouseDown,
105  	       	    NSEvent.EventType.leftMouseUp,
106  	       	    NSEvent.EventType.leftMouseDragged:
107  	           return 1;
108  	       case NSEvent.EventType.rightMouseDown,
109  	       	    NSEvent.EventType.rightMouseUp,
110  	            NSEvent.EventType.rightMouseDragged:
111  	           return 2;
112  	       case NSEvent.EventType.otherMouseDown,
113  	            NSEvent.EventType.otherMouseUp,
114  	            NSEvent.EventType.otherMouseDragged:
115  	           return 3;
116  	       default:
117  	           return 0;
118          }
119    }
120  
121  
122  
123    func mouse(with event: NSEvent, index idx:Int, type t:Int)
124    {
125  	var thepoint:NSPoint
126  	var button:Int
127  
128  	thepoint = event.locationInWindow
129  	button = get_mouse_button(with:event)
130  	/// button = event.buttonNumber
131  	/// print(" mouse down button \(event.buttonNumber) at location \(thepoint.x) x \(thepoint.y)")
132  	if (eventFuncts[idx] != nil)
133  	{
134  	  if (t == 0)
135  	   { _ = unsafeBitCast(eventFuncts[idx],to:(@convention(c)(Int32, Int32, Int32, UnsafeRawPointer)->Int32).self)(Int32(button), Int32(thepoint.x), Int32(CGFloat(size_y)-1.0-thepoint.y), eventParams[idx]) }
136  	  if (t == 1)
137  	   { _ = unsafeBitCast(eventFuncts[idx],to:(@convention(c)(Int32, Int32, UnsafeRawPointer)->Int32).self)(Int32(thepoint.x), Int32(CGFloat(size_y)-1.0-thepoint.y), eventParams[idx]) }
138  	}
139    }
140  
141    override func mouseDown(with event: NSEvent)  { mouse(with:event, index:4, type:0)  }
142    override func rightMouseDown(with event: NSEvent)   {	mouse(with:event, index:4, type:0)  }
143    override func otherMouseDown(with event: NSEvent)   {	mouse(with:event, index:4, type:0)  }
144  
145    override func mouseUp(with event: NSEvent)  { mouse(with:event, index:5, type:0)  }
146    override func rightMouseUp(with event: NSEvent)   { mouse(with:event, index:5, type:0)  }
147    override func otherMouseUp(with event: NSEvent)   { mouse(with:event, index:5, type:0)  }
148  
149    override func mouseMoved(with event: NSEvent)   { mouse(with:event, index:6, type:1)  }
150    override func mouseDragged(with event: NSEvent)   { mouse(with:event, index:6, type:1)  }
151    override func rightMouseDragged(with event: NSEvent)   { mouse(with:event, index:6, type:1)  }
152    override func otherMouseDragged(with event: NSEvent)   { mouse(with:event, index:6, type:1)  }
153  
154  
155    override func scrollWheel(with event: NSEvent)
156    {
157  	var thepoint:NSPoint
158  	var button = 0;
159  
160  	thepoint = event.locationInWindow
161  	if (event.deltaY > 0.2) { button = 4; }
162  	if (event.deltaY < -0.2) { button = 5; }
163  	if (event.deltaX > 0.2) { button = 6; }
164  	if (event.deltaX < -0.2) { button = 7; }
165          if (button != 0 && eventFuncts[4] != nil)
166          {
167            _ = unsafeBitCast(eventFuncts[4],to:(@convention(c)(Int32, Int32, Int32, UnsafeRawPointer)->Int32).self)(Int32(button), Int32(thepoint.x), Int32(thepoint.y), eventParams[4])
168          }
169    } 
170  
171  
172    override func flagsChanged(with event: NSEvent)
173    {
174  	var flag:UInt32
175  	var the_key:Int32
176  	var val:UInt32
177  
178  	flag = UInt32(event.modifierFlags.rawValue)
179  	val = (keyflag|flag)&(~(keyflag&flag))
180  	if (val == 0)
181  	    { return }   /// no change - can happen when loosing focus on special key pressed, then re-pressed later
182           the_key = 1
183  	 while (((val >> (the_key-1)) & 0x01)==0)
184  	  { the_key += 1 }
185  	 if (flag > keyflag && eventFuncts[2] != nil)
186  	   { _ = unsafeBitCast(eventFuncts[2],to:(@convention(c)(Int32, UnsafeRawPointer)->Int32).self)(0xFF+the_key, eventParams[2]) }
187  	 if (flag < keyflag && eventFuncts[3] != nil)
188  	   { _ = unsafeBitCast(eventFuncts[3],to:(@convention(c)(Int32, UnsafeRawPointer)->Int32).self)(0xFF+the_key, eventParams[3]) }
189  	 keyflag = flag
190    }
191  
192  
193    @objc func exposeNotification(_ notification:Notification)
194    {
195  	if (eventFuncts[12] != nil)
196  	{
197  	  _ = unsafeBitCast(eventFuncts[12],to:(@convention(c)(UnsafeRawPointer)->Int32).self)(eventParams[12])
198  	}
199    }
200  
201    @objc func closeNotification(_ notification:Notification)
202    {
203  	if (eventFuncts[17] != nil)
204  	{
205  	  _ = unsafeBitCast(eventFuncts[17],to:(@convention(c)(UnsafeRawPointer)->Int32).self)(eventParams[17])
206  	}
207    }
208  
209    @objc func deminiaturizeNotification(_ notification:Notification)
210    {
211  	exposeNotification(notification)
212    }
213  
214  }
215  
216  
217  
218  
219  
220  struct textureList
221  {
222     var uniformBuffer: MTLBuffer!
223     var uniform_data:UnsafeMutablePointer<Float>
224     unowned var image:MlxImg
225  }
226  
227  
228  public class MlxWin
229  {
230    let vrect: CGRect
231    var winE: WinEvent
232    var mlayer: CAMetalLayer
233  
234    unowned var device: MTLDevice
235    var commandQueue: MTLCommandQueue!
236    var pipelineState: MTLRenderPipelineState!
237    var vertexBuffer: MTLBuffer!
238  
239    var texture_list: Array<textureList> = Array()
240    var texture_list_count = 0
241  
242    var pixel_image:MlxImg
243    var pixel_count:Int
244  
245    var drawable_image: MlxImg
246    var uniq_renderPassDescriptor: MTLRenderPassDescriptor
247    var mtl_origin_null : MTLOrigin
248    var mtl_size_all : MTLSize
249    var doClear = false
250    var GPUbatch = 0
251  
252  
253    public init(device d:MTLDevice, width w:Int, height h:Int, title t:String)
254    {
255      vrect = CGRect(x: 100, y: 100, width: w, height: h)
256      winE = WinEvent(frame: vrect)
257  
258      device = d
259      mlayer = CAMetalLayer()
260      mlayer.device = device
261      mlayer.pixelFormat = .bgra8Unorm
262      mlayer.framebufferOnly = true
263      mlayer.contentsScale = 1.0 /// winE.screen!.backingScaleFactor
264      mlayer.frame = vrect
265      winE.contentView! = NSView(frame: vrect)
266      winE.contentView!.wantsLayer = true
267      winE.contentView!.layer = mlayer
268      winE.title = t
269      winE.isReleasedWhenClosed = false
270      winE.makeKeyAndOrderFront(nil)
271  
272  
273      /// drawable_image = MlxImg(d: device, w:Int(CGFloat(vrect.size.width)*winE.screen!.backingScaleFactor), h:Int(CGFloat(vrect.size.height)*winE.screen!.backingScaleFactor), t:1)
274      drawable_image = MlxImg(d: device, w:Int(vrect.size.width), h:Int(vrect.size.height), t:1)
275      pixel_image = MlxImg(d: device, w:Int(vrect.size.width), h:Int(vrect.size.height))
276      for i in 0...(pixel_image.texture_height*pixel_image.texture_sizeline/4-1)
277        { pixel_image.texture_data[i] = UInt32(0xFF000000) }
278      pixel_count = 0
279  
280      mtl_origin_null = MTLOriginMake(0,0,0)
281      mtl_size_all = MTLSizeMake(drawable_image.texture.width, drawable_image.texture.height, 1)
282  
283      uniq_renderPassDescriptor = MTLRenderPassDescriptor()
284      uniq_renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha:0.0)
285      uniq_renderPassDescriptor.colorAttachments[0].texture = drawable_image.texture
286      uniq_renderPassDescriptor.colorAttachments[0].storeAction = .store
287      uniq_renderPassDescriptor.colorAttachments[0].loadAction = .load
288    }
289  
290  
291  /// winEvent calls
292    public func convertToDisplay(_ pt:NSPoint) -> NSPoint  {
293    	 var pt2 = NSPoint(x:pt.x, y:pt.y)
294  	 pt2.y = CGFloat(winE.size_y)-1.0-pt2.y
295  	 pt2 = winE.convertPoint(toScreen: pt2)
296  	 pt2.y = (winE.screen!.frame.size.height)-pt2.y
297  	 return pt2
298  	 }
299    public func getWinEFrame() -> NSRect  { return winE.frame }
300    public func getScreenFrame() -> NSRect { return winE.screen!.frame }
301    public func getMouseLoc() -> NSPoint {
302    	 var pt = NSPoint()
303  	 pt = winE.mouseLocationOutsideOfEventStream
304  	 pt.y = CGFloat(winE.size_y)-1.0-pt.y
305  	 return pt
306  	 }
307    public func addHook(index idx:Int, fct fptr:UnsafeMutableRawPointer, param pptr:UnsafeMutableRawPointer)
308    {  winE.addHook(index: idx, fct: fptr, param: pptr)  }
309    public func setKeyRepeat(_ mode:Int)  { winE.setKeyRepeat(mode) }
310    public func destroyWinE()  { winE.close() }
311    public func setNotifs() { winE.setNotifs() }
312    public func delNotifs() { winE.delNotifs() }
313  
314  
315    public func initMetal()
316    {
317      commandQueue = device.makeCommandQueue()!
318  
319      /// vertex buffer & shaders stay the always the same.
320      let lib = try! device.makeLibrary(source: shaders, options: nil)
321      let vertexFunction = lib.makeFunction(name: "basic_vertex_function")
322      let fragmentFunction = lib.makeFunction(name: "basic_fragment_function")
323      let pipelineDesc = MTLRenderPipelineDescriptor()
324      pipelineDesc.colorAttachments[0].pixelFormat = .bgra8Unorm
325      pipelineDesc.colorAttachments[0].isBlendingEnabled = true
326      pipelineDesc.colorAttachments[0].rgbBlendOperation = .add
327      pipelineDesc.colorAttachments[0].alphaBlendOperation = .add
328      pipelineDesc.colorAttachments[0].sourceRGBBlendFactor = .oneMinusSourceAlpha
329      pipelineDesc.colorAttachments[0].sourceAlphaBlendFactor = .oneMinusSourceAlpha
330      pipelineDesc.colorAttachments[0].destinationRGBBlendFactor = .sourceAlpha
331      pipelineDesc.colorAttachments[0].destinationAlphaBlendFactor = .sourceAlpha
332      pipelineDesc.vertexFunction = vertexFunction
333      pipelineDesc.fragmentFunction = fragmentFunction
334      pipelineState = try! device.makeRenderPipelineState(descriptor: pipelineDesc)
335  
336      let vertexData: [Float] = [
337         -1.0, -1.0, 0.0, 1.0,  0.0, 1.0, 0.0, 0.0,
338         -1.0, 1.0, 0.0, 1.0,   0.0, 0.0, 0.0, 0.0,
339         1.0, -1.0, 0.0, 1.0,   1.0, 1.0, 0.0, 0.0,
340         1.0, -1.0, 0.0, 1.0,   1.0, 1.0, 0.0, 0.0,
341         -1.0, 1.0, 0.0, 1.0,   0.0, 0.0, 0.0, 0.0,
342         1.0, 1.0, 0.0, 1.0,    1.0, 0.0, 0.0, 0.0  ]
343      var dataSize = vertexData.count * MemoryLayout.size(ofValue: vertexData[0])
344      vertexBuffer = device.makeBuffer(bytes: vertexData, length: dataSize, options: []) 
345  
346      let uniformData: [Float] = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Float(vrect.size.width), Float(vrect.size.height), 0.0, 0.0, 0.0, 0.0,
347      		     	       1.0, 1.0, 1.0, 1.0 ]
348      dataSize = uniformData.count * MemoryLayout.size(ofValue: uniformData[0])
349      for _ in 0...255
350      { 
351        let uniformBuffer = device.makeBuffer(bytes: uniformData, length: dataSize, options: [])!
352        let uniform_data = (uniformBuffer.contents()).assumingMemoryBound(to:Float.self)
353        texture_list.append(textureList(uniformBuffer:uniformBuffer, uniform_data:uniform_data, image:pixel_image))
354      }
355  
356      self.clearWin();
357    }
358  
359  
360    public func clearWin()
361    {
362  	/// discard previous put_images, doClear become first operation in next render pass.
363  	var i = 0
364  	while i < texture_list_count
365  	{
366  		texture_list[i].image.onGPU -= 1
367  		i += 1
368  	}
369  	texture_list_count = 0
370  	doClear = true
371  	///  next flush images should call draw(), even if there is no image to put
372    }
373  
374    func flushPixels()
375    {
376  	if (pixel_count > 0)
377  	{
378  	  pixel_count = 0
379  	  self.putImage(image:pixel_image, x:0, y:0)
380  	}
381    }
382  
383    public func flushImages()
384    {
385  	flushPixels()
386  	if (texture_list_count > 0 || doClear)
387  	 {
388  	    self.draw()
389  	 }
390    }
391  
392    public func waitForGPU()
393    {
394  	while (GPUbatch > 0) { }
395    }
396  
397  
398    public func pixelPut(_ x:Int32, _ y:Int32, _ color:UInt32)
399    {
400  	if (pixel_count == 0)
401  	{
402  	  while (pixel_image.onGPU > 0)
403  	  {
404  	     if (GPUbatch > 0) { waitForGPU() }
405  	     else { flushImages() }
406  	  }
407  	  for i in 0...pixel_image.texture_height*pixel_image.texture_sizeline/4-1
408  	    { pixel_image.texture_data[i] = UInt32(0xFF000000) }
409  	}
410  	let t = (x&(Int32(vrect.size.width-1)-x))&(y&(Int32(vrect.size.height-1)-y))
411  	if t >= 0
412  	{
413  		pixel_image.texture_data[Int(y)*pixel_image.texture_sizeline/4+Int(x)] = color
414  		pixel_count += 1
415  	}
416    }
417  
418    public func putImage(image img:MlxImg, x posx:Int32, y posy:Int32)
419    {
420  	flushPixels()
421  	putImageScale(image:img, sx:0, sy:0, sw:Int32(img.texture_width), sh:Int32(img.texture_height), 
422  			   dx:posx, dy:posy, dw:Int32(img.texture_width), dh:Int32(img.texture_height),
423  			   c:UInt32(0xFFFFFFFF))
424    }
425  
426    public func putImageScale(image img:MlxImg, sx src_x:Int32, sy src_y:Int32, sw src_w:Int32, sh src_h:Int32, dx dest_x:Int32, dy dest_y:Int32, dw dest_w:Int32, dh dest_h:Int32, c color:UInt32)
427    {
428  	flushPixels()
429  	if (texture_list_count == 0) /// means  I just draw
430  	{
431  		waitForGPU()    /// to be able to write again in uniforms
432  	}
433  	texture_list[texture_list_count].uniform_data[0] = Float(img.texture_width)
434  	texture_list[texture_list_count].uniform_data[1] = Float(img.texture_height)
435  	texture_list[texture_list_count].uniform_data[2] = Float(src_x)
436  	texture_list[texture_list_count].uniform_data[3] = Float(src_y)
437  	texture_list[texture_list_count].uniform_data[4] = Float(src_w)
438  	texture_list[texture_list_count].uniform_data[5] = Float(src_h)
439  
440  	texture_list[texture_list_count].uniform_data[8] = Float(dest_x)
441  	texture_list[texture_list_count].uniform_data[9] = Float(dest_y)
442  	texture_list[texture_list_count].uniform_data[10] = Float(dest_w)
443  	texture_list[texture_list_count].uniform_data[11] = Float(dest_h)
444  
445  	texture_list[texture_list_count].uniform_data[12] = Float((color>>16)&0xFF)/255.0;
446  	texture_list[texture_list_count].uniform_data[13] = Float((color>>8)&0xFF)/255.0;
447  	texture_list[texture_list_count].uniform_data[14] = Float((color>>0)&0xFF)/255.0;
448  	texture_list[texture_list_count].uniform_data[15] = Float((color>>24)&0xFF)/255.0;
449  
450  	texture_list[texture_list_count].image = img
451  	img.onGPU += 1
452  	
453  	texture_list_count += 1
454  	if (texture_list_count == 255) /// keep 1 slot for put_pixels image
455  	{
456  		flushImages()
457  	}
458    }
459  
460  
461    func draw()
462    {
463  	var commandBuffer = commandQueue.makeCommandBuffer()!
464  
465  /// clear if asked
466  	if (doClear)
467  	{
468  	  uniq_renderPassDescriptor.colorAttachments[0].loadAction = .clear
469  	  let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: uniq_renderPassDescriptor)!
470  	  commandEncoder.endEncoding()
471  	  uniq_renderPassDescriptor.colorAttachments[0].loadAction = .load
472  	  doClear = false
473  	}
474  
475  /// then draw the images if any.
476  	var i = 0
477  	while i < texture_list_count
478  	{
479  		let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: uniq_renderPassDescriptor)!
480  		commandEncoder.setRenderPipelineState(pipelineState)
481  		commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
482  		commandEncoder.setVertexBuffer(texture_list[i].uniformBuffer, offset: 0, index: 1)
483  		commandEncoder.setFragmentTexture(texture_list[i].image.texture, index: 0)
484  		commandEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 6, instanceCount:2)
485  		commandEncoder.endEncoding()
486  		({ j in
487  		      commandBuffer.addCompletedHandler { cb in self.texture_list[j].image.onGPU -= 1 }
488  		})(i)
489  		i += 1
490  	}
491  	texture_list_count = 0
492  	commandBuffer.addCompletedHandler { cb in self.GPUbatch -= 1 }
493          commandBuffer.commit()
494  	GPUbatch += 1
495  
496  /// finally copy to MTLdrawable to present, using a new commandqueue
497      	commandBuffer = commandQueue.makeCommandBuffer()!
498  	let curdraw = mlayer.nextDrawable()!
499  
500  	let commandBEncoder = commandBuffer.makeBlitCommandEncoder()!
501  	commandBEncoder.copy(from:drawable_image.texture, sourceSlice:0, sourceLevel:0, sourceOrigin: mtl_origin_null, sourceSize: mtl_size_all,  to:curdraw.texture, destinationSlice:0, destinationLevel:0, destinationOrigin: mtl_origin_null)
502  	commandBEncoder.endEncoding()
503  
504  	commandBuffer.addCompletedHandler { cb in self.GPUbatch -= 1 }
505  	commandBuffer.present(curdraw)
506          commandBuffer.commit()
507  	GPUbatch += 1
508    }
509  
510  
511  }
512  
513  
514  
515  
516  let shaders = """
517  #include <metal_stdlib>
518  using namespace metal;
519  
520  struct VertexIn {
521      float4 position;
522      float4 UV;
523  };
524  struct VertexOut {
525      float4 position [[ position ]];
526      float4 color;
527      float2 UV;
528  };
529  struct uniforms {
530     packed_float2 origin_size;
531     packed_float2 origin_pos;
532     packed_float2 origin_sub;
533     packed_float2 dest_size;
534     packed_float2 dest_pos;
535     packed_float2 dest_sub;
536     packed_float4 color;
537  };
538  vertex VertexOut basic_vertex_function(const device VertexIn *vertices [[ buffer(0) ]], constant uniforms& uni [[ buffer(1) ]],
539  uint vertexID [[ vertex_id ]])
540  {
541      VertexOut vOut;
542      float4 start = float4((2.0*uni.dest_pos.x)/(uni.dest_size.x-1.0) - 1.0, 1.0 - (2.0*uni.dest_pos.y)/(uni.dest_size.y-1.0) - (uni.dest_sub.y*2.0)/uni.dest_size.y, 0.0, 0.0);
543   /*   vOut.position = (start + (vertices[vertexID].position + 1.0) * float4(uni.dest_sub, 0.0, 0.0))/float4(uni.dest_size, 1.0, 1.0); */
544  
545      vOut.position = float4(start.x+((vertices[vertexID].position.x + 1.0)*uni.dest_sub.x)/(uni.dest_size.x),
546      		    	   start.y+((vertices[vertexID].position.y + 1.0)*uni.dest_sub.y)/(uni.dest_size.y), 0.0, 1.0);
547  
548      vOut.UV = (uni.origin_pos + float2(vertices[vertexID].UV.x, vertices[vertexID].UV.y)*(uni.origin_sub-1.0))/(uni.origin_size-1.0);
549      vOut.color = uni.color;
550      return vOut;
551  }
552  fragment float4 basic_fragment_function(VertexOut vIn [[ stage_in ]], texture2d<float> texture [[ texture(0) ]])
553  {
554      constexpr sampler textureSampler(address::clamp_to_edge);
555      return vIn.color*texture.sample(textureSampler, vIn.UV);
556  }
557  """
558