github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/exp/shiny/driver/gldriver/window.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gldriver
     6  
     7  import (
     8  	"image"
     9  	"image/color"
    10  	"image/draw"
    11  	"sync"
    12  
    13  	"golang.org/x/exp/shiny/driver/internal/pump"
    14  	"golang.org/x/exp/shiny/screen"
    15  	"golang.org/x/image/math/f64"
    16  	"golang.org/x/mobile/event/lifecycle"
    17  	"golang.org/x/mobile/event/size"
    18  	"golang.org/x/mobile/gl"
    19  )
    20  
    21  type windowImpl struct {
    22  	s *screenImpl
    23  
    24  	// id is a C data structure for the window.
    25  	//	- For Cocoa, it's a ScreenGLView*.
    26  	//	- For X11, it's a Window.
    27  	id uintptr
    28  
    29  	// ctx is a C data structure for the GL context.
    30  	//	- For Cocoa, it's a NSOpenGLContext*.
    31  	//	- For X11, it's an EGLSurface.
    32  	ctx uintptr
    33  
    34  	lifecycleStage lifecycle.Stage // current stage
    35  
    36  	pump        pump.Pump
    37  	publish     chan struct{}
    38  	publishDone chan screen.PublishResult
    39  	drawDone    chan struct{}
    40  
    41  	// glctxMu is a mutex that enforces the atomicity of methods like
    42  	// Texture.Upload or Window.Draw that are conceptually one operation
    43  	// but are implemented by multiple OpenGL calls. OpenGL is a stateful
    44  	// API, so interleaving OpenGL calls from separate higher-level
    45  	// operations causes inconsistencies.
    46  	glctxMu sync.Mutex
    47  	glctx   gl.Context
    48  	worker  gl.Worker
    49  
    50  	szMu sync.Mutex
    51  	sz   size.Event
    52  }
    53  
    54  func (w *windowImpl) Release() {
    55  	// There are two ways a window can be closed. The first is the user
    56  	// clicks the red button, in which case windowWillClose is called,
    57  	// which calls Go's windowClosing, which does cleanup in
    58  	// releaseCleanup below.
    59  	//
    60  	// The second way is Release is called programmatically. This calls
    61  	// the NSWindow method performClose, which emulates the red button
    62  	// being clicked.
    63  	//
    64  	// If these two approaches race, experiments suggest it is resolved
    65  	// by performClose (which is called serially on the main thread).
    66  	// If that stops being true, there is a check in windowWillClose
    67  	// that avoids the Go cleanup code being invoked more than once.
    68  	closeWindow(w.id)
    69  }
    70  
    71  func (w *windowImpl) releaseCleanup() {
    72  	w.pump.Release()
    73  }
    74  
    75  func (w *windowImpl) Events() <-chan interface{} { return w.pump.Events() }
    76  func (w *windowImpl) Send(event interface{})     { w.pump.Send(event) }
    77  
    78  func (w *windowImpl) Upload(dp image.Point, src screen.Buffer, sr image.Rectangle) {
    79  	// TODO: adjust if dp is outside dst bounds, or sr is outside src bounds.
    80  	// TODO: keep a texture around for this purpose?
    81  	t, err := w.s.NewTexture(sr.Size())
    82  	if err != nil {
    83  		panic(err)
    84  	}
    85  	t.Upload(dp, src, sr)
    86  	w.Draw(f64.Aff3{
    87  		1, 0, float64(dp.X),
    88  		0, 1, float64(dp.Y),
    89  	}, t, sr, draw.Src, nil)
    90  	t.Release()
    91  }
    92  
    93  func (w *windowImpl) Fill(dr image.Rectangle, src color.Color, op draw.Op) {
    94  	w.glctxMu.Lock()
    95  	defer w.glctxMu.Unlock()
    96  
    97  	if !w.glctx.IsProgram(w.s.fill.program) {
    98  		p, err := compileProgram(w.glctx, fillVertexSrc, fillFragmentSrc)
    99  		if err != nil {
   100  			// TODO: initialize this somewhere else we can better handle the error.
   101  			panic(err.Error())
   102  		}
   103  		w.s.fill.program = p
   104  		w.s.fill.pos = w.glctx.GetAttribLocation(p, "pos")
   105  		w.s.fill.mvp = w.glctx.GetUniformLocation(p, "mvp")
   106  		w.s.fill.color = w.glctx.GetUniformLocation(p, "color")
   107  		w.s.fill.quad = w.glctx.CreateBuffer()
   108  
   109  		w.glctx.BindBuffer(gl.ARRAY_BUFFER, w.s.fill.quad)
   110  		w.glctx.BufferData(gl.ARRAY_BUFFER, quadCoords, gl.STATIC_DRAW)
   111  	}
   112  	w.glctx.UseProgram(w.s.fill.program)
   113  
   114  	dstL := float64(dr.Min.X)
   115  	dstT := float64(dr.Min.Y)
   116  	dstR := float64(dr.Max.X)
   117  	dstB := float64(dr.Max.Y)
   118  	writeAff3(w.glctx, w.s.fill.mvp, w.mvp(
   119  		dstL, dstT,
   120  		dstR, dstT,
   121  		dstL, dstB,
   122  	))
   123  
   124  	r, g, b, a := src.RGBA()
   125  	w.glctx.Uniform4f(
   126  		w.s.fill.color,
   127  		float32(r)/65535,
   128  		float32(g)/65535,
   129  		float32(b)/65535,
   130  		float32(a)/65535,
   131  	)
   132  
   133  	w.glctx.BindBuffer(gl.ARRAY_BUFFER, w.s.fill.quad)
   134  	w.glctx.EnableVertexAttribArray(w.s.fill.pos)
   135  	w.glctx.VertexAttribPointer(w.s.fill.pos, 2, gl.FLOAT, false, 0, 0)
   136  
   137  	w.glctx.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
   138  
   139  	w.glctx.DisableVertexAttribArray(w.s.fill.pos)
   140  }
   141  
   142  func (w *windowImpl) Draw(src2dst f64.Aff3, src screen.Texture, sr image.Rectangle, op draw.Op, opts *screen.DrawOptions) {
   143  	w.glctxMu.Lock()
   144  	defer w.glctxMu.Unlock()
   145  
   146  	w.glctx.UseProgram(w.s.texture.program)
   147  
   148  	// Start with src-space left, top, right and bottom.
   149  	srcL := float64(sr.Min.X)
   150  	srcT := float64(sr.Min.Y)
   151  	srcR := float64(sr.Max.X)
   152  	srcB := float64(sr.Max.Y)
   153  	// Transform to dst-space via the src2dst matrix, then to a MVP matrix.
   154  	writeAff3(w.glctx, w.s.texture.mvp, w.mvp(
   155  		src2dst[0]*srcL+src2dst[1]*srcT+src2dst[2],
   156  		src2dst[3]*srcL+src2dst[4]*srcT+src2dst[5],
   157  		src2dst[0]*srcR+src2dst[1]*srcT+src2dst[2],
   158  		src2dst[3]*srcR+src2dst[4]*srcT+src2dst[5],
   159  		src2dst[0]*srcL+src2dst[1]*srcB+src2dst[2],
   160  		src2dst[3]*srcL+src2dst[4]*srcB+src2dst[5],
   161  	))
   162  
   163  	// OpenGL's fragment shaders' UV coordinates run from (0,0)-(1,1),
   164  	// unlike vertex shaders' XY coordinates running from (-1,+1)-(+1,-1).
   165  	//
   166  	// We are drawing a rectangle PQRS, defined by two of its
   167  	// corners, onto the entire texture. The two quads may actually
   168  	// be equal, but in the general case, PQRS can be smaller.
   169  	//
   170  	//	(0,0) +---------------+ (1,0)
   171  	//	      |  P +-----+ Q  |
   172  	//	      |    |     |    |
   173  	//	      |  S +-----+ R  |
   174  	//	(0,1) +---------------+ (1,1)
   175  	//
   176  	// The PQRS quad is always axis-aligned. First of all, convert
   177  	// from pixel space to texture space.
   178  	t := src.(*textureImpl)
   179  	tw := float64(t.size.X)
   180  	th := float64(t.size.Y)
   181  	px := float64(sr.Min.X-0) / tw
   182  	py := float64(sr.Min.Y-0) / th
   183  	qx := float64(sr.Max.X-0) / tw
   184  	sy := float64(sr.Max.Y-0) / th
   185  	// Due to axis alignment, qy = py and sx = px.
   186  	//
   187  	// The simultaneous equations are:
   188  	//	  0 +   0 + a02 = px
   189  	//	  0 +   0 + a12 = py
   190  	//	a00 +   0 + a02 = qx
   191  	//	a10 +   0 + a12 = qy = py
   192  	//	  0 + a01 + a02 = sx = px
   193  	//	  0 + a11 + a12 = sy
   194  	writeAff3(w.glctx, w.s.texture.uvp, f64.Aff3{
   195  		qx - px, 0, px,
   196  		0, sy - py, py,
   197  	})
   198  
   199  	w.glctx.ActiveTexture(gl.TEXTURE0)
   200  	w.glctx.BindTexture(gl.TEXTURE_2D, t.id)
   201  	w.glctx.Uniform1i(w.s.texture.sample, 0)
   202  
   203  	w.glctx.BindBuffer(gl.ARRAY_BUFFER, w.s.texture.quad)
   204  	w.glctx.EnableVertexAttribArray(w.s.texture.pos)
   205  	w.glctx.VertexAttribPointer(w.s.texture.pos, 2, gl.FLOAT, false, 0, 0)
   206  
   207  	w.glctx.BindBuffer(gl.ARRAY_BUFFER, w.s.texture.quad)
   208  	w.glctx.EnableVertexAttribArray(w.s.texture.inUV)
   209  	w.glctx.VertexAttribPointer(w.s.texture.inUV, 2, gl.FLOAT, false, 0, 0)
   210  
   211  	w.glctx.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
   212  
   213  	w.glctx.DisableVertexAttribArray(w.s.texture.pos)
   214  	w.glctx.DisableVertexAttribArray(w.s.texture.inUV)
   215  }
   216  
   217  // mvp returns the Model View Projection matrix that maps the quadCoords unit
   218  // square, (0, 0) to (1, 1), to a quad QV, such that QV in vertex shader space
   219  // corresponds to the quad QP in pixel space, where QP is defined by three of
   220  // its four corners - the arguments to this function. The three corners are
   221  // nominally the top-left, top-right and bottom-left, but there is no
   222  // constraint that e.g. tlx < trx.
   223  //
   224  // In pixel space, the window ranges from (0, 0) to (sz.WidthPx, sz.HeightPx).
   225  // The Y-axis points downwards.
   226  //
   227  // In vertex shader space, the window ranges from (-1, +1) to (+1, -1), which
   228  // is a 2-unit by 2-unit square. The Y-axis points upwards.
   229  func (w *windowImpl) mvp(tlx, tly, trx, try, blx, bly float64) f64.Aff3 {
   230  	w.szMu.Lock()
   231  	sz := w.sz
   232  	w.szMu.Unlock()
   233  
   234  	// Convert from pixel coords to vertex shader coords.
   235  	invHalfWidth := +2 / float64(sz.WidthPx)
   236  	invHalfHeight := -2 / float64(sz.HeightPx)
   237  	tlx = tlx*invHalfWidth - 1
   238  	tly = tly*invHalfHeight + 1
   239  	trx = trx*invHalfWidth - 1
   240  	try = try*invHalfHeight + 1
   241  	blx = blx*invHalfWidth - 1
   242  	bly = bly*invHalfHeight + 1
   243  
   244  	// The resultant affine matrix:
   245  	//	- maps (0, 0) to (tlx, tly).
   246  	//	- maps (1, 0) to (trx, try).
   247  	//	- maps (0, 1) to (blx, bly).
   248  	return f64.Aff3{
   249  		trx - tlx, blx - tlx, tlx,
   250  		try - tly, bly - tly, tly,
   251  	}
   252  }
   253  
   254  func (w *windowImpl) Publish() screen.PublishResult {
   255  	// gl.Flush is a lightweight (on modern GL drivers) blocking call
   256  	// that ensures all GL functions pending in the gl package have
   257  	// been passed onto the GL driver before the app package attempts
   258  	// to swap the screen buffer.
   259  	//
   260  	// This enforces that the final receive (for this paint cycle) on
   261  	// gl.WorkAvailable happens before the send on publish.
   262  	w.glctxMu.Lock()
   263  	w.glctx.Flush()
   264  	w.glctxMu.Unlock()
   265  
   266  	w.publish <- struct{}{}
   267  	res := <-w.publishDone
   268  
   269  	select {
   270  	case w.drawDone <- struct{}{}:
   271  	default:
   272  	}
   273  
   274  	return res
   275  }