// 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 provides an OpenGL driver for accessing a screen. package gldriver // import "golang.org/x/exp/shiny/driver/gldriver" import ( "encoding/binary" "fmt" "math" "golang.org/x/exp/shiny/driver/internal/errscreen" "golang.org/x/exp/shiny/screen" "golang.org/x/image/math/f64" "golang.org/x/mobile/gl" ) // Main is called by the program's main function to run the graphical // application. // // It calls f on the Screen, possibly in a separate goroutine, as some OS- // specific libraries require being on 'the main thread'. It returns when f // returns. func Main(f func(screen.Screen)) { if err := main(f); err != nil { f(errscreen.Stub(err)) } } func mul(a, b f64.Aff3) f64.Aff3 { return f64.Aff3{ a[0]*b[0] + a[1]*b[3], a[0]*b[1] + a[1]*b[4], a[0]*b[2] + a[1]*b[5] + a[2], a[3]*b[0] + a[4]*b[3], a[3]*b[1] + a[4]*b[4], a[3]*b[2] + a[4]*b[5] + a[5], } } // writeAff3 must only be called while holding windowImpl.glctxMu. func writeAff3(glctx gl.Context, u gl.Uniform, a f64.Aff3) { var m [9]float32 m[0*3+0] = float32(a[0*3+0]) m[0*3+1] = float32(a[1*3+0]) m[0*3+2] = 0 m[1*3+0] = float32(a[0*3+1]) m[1*3+1] = float32(a[1*3+1]) m[1*3+2] = 0 m[2*3+0] = float32(a[0*3+2]) m[2*3+1] = float32(a[1*3+2]) m[2*3+2] = 1 glctx.UniformMatrix3fv(u, m[:]) } // f32Bytes returns the byte representation of float32 values in the given byte // order. byteOrder must be either binary.BigEndian or binary.LittleEndian. func f32Bytes(byteOrder binary.ByteOrder, values ...float32) []byte { le := false switch byteOrder { case binary.BigEndian: case binary.LittleEndian: le = true default: panic(fmt.Sprintf("invalid byte order %v", byteOrder)) } b := make([]byte, 4*len(values)) for i, v := range values { u := math.Float32bits(v) if le { b[4*i+0] = byte(u >> 0) b[4*i+1] = byte(u >> 8) b[4*i+2] = byte(u >> 16) b[4*i+3] = byte(u >> 24) } else { b[4*i+0] = byte(u >> 24) b[4*i+1] = byte(u >> 16) b[4*i+2] = byte(u >> 8) b[4*i+3] = byte(u >> 0) } } return b } // compileProgram must only be called while holding windowImpl.glctxMu. func compileProgram(glctx gl.Context, vSrc, fSrc string) (gl.Program, error) { program := glctx.CreateProgram() if program.Value == 0 { return gl.Program{}, fmt.Errorf("gldriver: no programs available") } vertexShader, err := compileShader(glctx, gl.VERTEX_SHADER, vSrc) if err != nil { return gl.Program{}, err } fragmentShader, err := compileShader(glctx, gl.FRAGMENT_SHADER, fSrc) if err != nil { glctx.DeleteShader(vertexShader) return gl.Program{}, err } glctx.AttachShader(program, vertexShader) glctx.AttachShader(program, fragmentShader) glctx.LinkProgram(program) // Flag shaders for deletion when program is unlinked. glctx.DeleteShader(vertexShader) glctx.DeleteShader(fragmentShader) if glctx.GetProgrami(program, gl.LINK_STATUS) == 0 { defer glctx.DeleteProgram(program) return gl.Program{}, fmt.Errorf("gldriver: program compile: %s", glctx.GetProgramInfoLog(program)) } return program, nil } // compileShader must only be called while holding windowImpl.glctxMu. func compileShader(glctx gl.Context, shaderType gl.Enum, src string) (gl.Shader, error) { shader := glctx.CreateShader(shaderType) if shader.Value == 0 { return gl.Shader{}, fmt.Errorf("gldriver: could not create shader (type %v)", shaderType) } glctx.ShaderSource(shader, src) glctx.CompileShader(shader) if glctx.GetShaderi(shader, gl.COMPILE_STATUS) == 0 { defer glctx.DeleteShader(shader) return gl.Shader{}, fmt.Errorf("gldriver: shader compile: %s", glctx.GetShaderInfoLog(shader)) } return shader, nil }