blob: f6f18f1bf7963283dc228ddae2ee10cfa801e3fb [file] [log] [blame]
// Copyright 2015 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.
package gldriver
import (
"fmt"
"image"
"sync"
"golang.org/x/exp/shiny/screen"
"golang.org/x/mobile/gl"
)
var theScreen = &screenImpl{
windows: make(map[uintptr]*windowImpl),
}
type screenImpl struct {
texture struct {
program gl.Program
pos gl.Attrib
mvp gl.Uniform
uvp gl.Uniform
inUV gl.Attrib
sample gl.Uniform
quad gl.Buffer
}
fill struct {
program gl.Program
pos gl.Attrib
mvp gl.Uniform
color gl.Uniform
quad gl.Buffer
}
mu sync.Mutex
windows map[uintptr]*windowImpl
}
func (s *screenImpl) NewBuffer(size image.Point) (retBuf screen.Buffer, retErr error) {
m := image.NewRGBA(image.Rectangle{Max: size})
return &bufferImpl{
buf: m.Pix,
rgba: *m,
size: size,
}, nil
}
func (s *screenImpl) NewTexture(size image.Point) (screen.Texture, error) {
// TODO: can we compile these programs eagerly instead of lazily?
// Find a GL context for this texture.
// TODO: this might be correct. Some GL objects can be shared
// across contexts. But this needs a review of the spec to make
// sure it's correct, and some testing would be nice.
var w *windowImpl
s.mu.Lock()
for _, window := range s.windows {
w = window
break
}
s.mu.Unlock()
if w == nil {
return nil, fmt.Errorf("gldriver: no window available")
}
w.glctxMu.Lock()
defer w.glctxMu.Unlock()
glctx := w.glctx
if glctx == nil {
return nil, fmt.Errorf("gldriver: no GL context available")
}
if !glctx.IsProgram(s.texture.program) {
p, err := compileProgram(glctx, textureVertexSrc, textureFragmentSrc)
if err != nil {
return nil, err
}
s.texture.program = p
s.texture.pos = glctx.GetAttribLocation(p, "pos")
s.texture.mvp = glctx.GetUniformLocation(p, "mvp")
s.texture.uvp = glctx.GetUniformLocation(p, "uvp")
s.texture.inUV = glctx.GetAttribLocation(p, "inUV")
s.texture.sample = glctx.GetUniformLocation(p, "sample")
s.texture.quad = glctx.CreateBuffer()
glctx.BindBuffer(gl.ARRAY_BUFFER, s.texture.quad)
glctx.BufferData(gl.ARRAY_BUFFER, quadCoords, gl.STATIC_DRAW)
}
t := &textureImpl{
w: w,
id: glctx.CreateTexture(),
size: size,
}
glctx.BindTexture(gl.TEXTURE_2D, t.id)
glctx.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size.X, size.Y, gl.RGBA, gl.UNSIGNED_BYTE, nil)
glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
glctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
return t, nil
}
func optsSize(opts *screen.NewWindowOptions) (width, height int) {
width, height = 1024, 768
if opts != nil {
if opts.Width > 0 {
width = opts.Width
}
if opts.Height > 0 {
height = opts.Height
}
}
return width, height
}
func (s *screenImpl) NewWindow(opts *screen.NewWindowOptions) (screen.Window, error) {
id, err := newWindow(opts)
if err != nil {
return nil, err
}
w := &windowImpl{
s: s,
id: id,
publish: make(chan struct{}),
publishDone: make(chan screen.PublishResult),
drawDone: make(chan struct{}),
}
initWindow(w)
s.mu.Lock()
s.windows[id] = w
s.mu.Unlock()
if useLifecycler {
w.lifecycler.SendEvent(w, nil)
}
showWindow(w)
return w, nil
}