screen.go (3478B)
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 "fmt" 9 "image" 10 "sync" 11 12 "golang.org/x/exp/shiny/screen" 13 "golang.org/x/mobile/gl" 14 ) 15 16 var theScreen = &screenImpl{ 17 windows: make(map[uintptr]*windowImpl), 18 } 19 20 type screenImpl struct { 21 texture struct { 22 program gl.Program 23 pos gl.Attrib 24 mvp gl.Uniform 25 uvp gl.Uniform 26 inUV gl.Attrib 27 sample gl.Uniform 28 quad gl.Buffer 29 } 30 fill struct { 31 program gl.Program 32 pos gl.Attrib 33 mvp gl.Uniform 34 color gl.Uniform 35 quad gl.Buffer 36 } 37 38 mu sync.Mutex 39 windows map[uintptr]*windowImpl 40 } 41 42 func (s *screenImpl) NewBuffer(size image.Point) (retBuf screen.Buffer, retErr error) { 43 m := image.NewRGBA(image.Rectangle{Max: size}) 44 return &bufferImpl{ 45 buf: m.Pix, 46 rgba: *m, 47 size: size, 48 }, nil 49 } 50 51 func (s *screenImpl) NewTexture(size image.Point) (screen.Texture, error) { 52 // TODO: can we compile these programs eagerly instead of lazily? 53 54 // Find a GL context for this texture. 55 // TODO: this might be correct. Some GL objects can be shared 56 // across contexts. But this needs a review of the spec to make 57 // sure it's correct, and some testing would be nice. 58 var w *windowImpl 59 60 s.mu.Lock() 61 for _, window := range s.windows { 62 w = window 63 break 64 } 65 s.mu.Unlock() 66 67 if w == nil { 68 return nil, fmt.Errorf("gldriver: no window available") 69 } 70 71 w.glctxMu.Lock() 72 defer w.glctxMu.Unlock() 73 glctx := w.glctx 74 if glctx == nil { 75 return nil, fmt.Errorf("gldriver: no GL context available") 76 } 77 78 if !glctx.IsProgram(s.texture.program) { 79 p, err := compileProgram(glctx, textureVertexSrc, textureFragmentSrc) 80 if err != nil { 81 return nil, err 82 } 83 s.texture.program = p 84 s.texture.pos = glctx.GetAttribLocation(p, "pos") 85 s.texture.mvp = glctx.GetUniformLocation(p, "mvp") 86 s.texture.uvp = glctx.GetUniformLocation(p, "uvp") 87 s.texture.inUV = glctx.GetAttribLocation(p, "inUV") 88 s.texture.sample = glctx.GetUniformLocation(p, "sample") 89 s.texture.quad = glctx.CreateBuffer() 90 91 glctx.BindBuffer(gl.ARRAY_BUFFER, s.texture.quad) 92 glctx.BufferData(gl.ARRAY_BUFFER, quadCoords, gl.STATIC_DRAW) 93 } 94 95 t := &textureImpl{ 96 w: w, 97 id: glctx.CreateTexture(), 98 size: size, 99 } 100 101 glctx.BindTexture(gl.TEXTURE_2D, t.id) 102 glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size.X, size.Y, gl.RGBA, gl.UNSIGNED_BYTE, nil) 103 glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) 104 glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) 105 glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) 106 glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) 107 108 return t, nil 109 } 110 111 func optsSize(opts *screen.NewWindowOptions) (width, height int) { 112 width, height = 1024, 768 113 if opts != nil { 114 if opts.Width > 0 { 115 width = opts.Width 116 } 117 if opts.Height > 0 { 118 height = opts.Height 119 } 120 } 121 return width, height 122 } 123 124 func (s *screenImpl) NewWindow(opts *screen.NewWindowOptions) (screen.Window, error) { 125 id, err := newWindow(opts) 126 if err != nil { 127 return nil, err 128 } 129 w := &windowImpl{ 130 s: s, 131 id: id, 132 publish: make(chan struct{}), 133 publishDone: make(chan screen.PublishResult), 134 drawDone: make(chan struct{}), 135 } 136 initWindow(w) 137 138 s.mu.Lock() 139 s.windows[id] = w 140 s.mu.Unlock() 141 142 if useLifecycler { 143 w.lifecycler.SendEvent(w, nil) 144 } 145 146 showWindow(w) 147 148 return w, nil 149 }