blob: b21a8a82b593257056825cadf9dfe83f60ebf1c2 [file] [log] [blame]
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build darwin
package mtldriver
import (
// windowImpl implements screen.Window.
type windowImpl struct {
device mtl.Device
window *glfw.Window
releaseWindowCh chan releaseWindowReq
ml coreanim.MetalLayer
cq mtl.CommandQueue
lifecycler lifecycler.State
rgba *image.RGBA
texture mtl.Texture // Used in Publish.
func (w *windowImpl) Release() {
respCh := make(chan struct{})
w.releaseWindowCh <- releaseWindowReq{
window: w.window,
respCh: respCh,
glfw.PostEmptyEvent() // Break main loop out of glfw.WaitEvents so it can receive on releaseWindowCh.
func (w *windowImpl) NextEvent() interface{} {
e := w.Deque.NextEvent()
if sz, ok := e.(size.Event); ok {
// TODO(dmitshur): this is the best place/time/frequency to do this
// I've found so far, but see if it can be even better
// Set drawable size, create backing image and texture., sz.HeightPx)
w.rgba = image.NewRGBA(image.Rectangle{Max: image.Point{X: sz.WidthPx, Y: sz.HeightPx}})
w.texture = w.device.MakeTexture(mtl.TextureDescriptor{
PixelFormat: mtl.PixelFormatRGBA8UNorm,
Width: sz.WidthPx,
Height: sz.HeightPx,
StorageMode: mtl.StorageModeManaged,
return e
func (w *windowImpl) Publish() screen.PublishResult {
// Copy w.rgba pixels into a texture.
region := mtl.RegionMake2D(0, 0, w.texture.Width, w.texture.Height)
bytesPerRow := 4 * w.texture.Width
w.texture.ReplaceRegion(region, 0, &w.rgba.Pix[0], uintptr(bytesPerRow))
drawable, err :=
if err != nil {
log.Println("Window.Publish: couldn't get the next drawable:", err)
return screen.PublishResult{}
cb := w.cq.MakeCommandBuffer()
// Copy the texture into the drawable.
bce := cb.MakeBlitCommandEncoder()
w.texture, 0, 0, mtl.Origin{}, mtl.Size{w.texture.Width, w.texture.Height, 1},
drawable.Texture(), 0, 0, mtl.Origin{})
return screen.PublishResult{}
func (w *windowImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) {
draw.Draw(w.rgba, sr.Sub(sr.Min).Add(dp), src.RGBA(), sr.Min, draw.Src)
func (w *windowImpl) Fill(dr image.Rectangle, src color.Color, op draw.Op) {
draw.Draw(w.rgba, dr, &image.Uniform{src}, image.Point{}, op)
func (w *windowImpl) Draw(src2dst f64.Aff3, src screen.Texture, sr image.Rectangle, op draw.Op, _ *screen.DrawOptions) {
draw.NearestNeighbor.Transform(w.rgba, src2dst, src.(*textureImpl).rgba, sr, op, nil)
func (w *windowImpl) DrawUniform(src2dst f64.Aff3, src color.Color, sr image.Rectangle, op draw.Op, _ *screen.DrawOptions) {
draw.NearestNeighbor.Transform(w.rgba, src2dst, &image.Uniform{src}, sr, op, nil)
func (w *windowImpl) Copy(dp image.Point, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
drawer.Copy(w, dp, src, sr, op, opts)
func (w *windowImpl) Scale(dr image.Rectangle, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
drawer.Scale(w, dr, src, sr, op, opts)