David Crawshaw | cabcb38 | 2015-07-30 14:52:09 -0400 | [diff] [blame] | 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 provides an OpenGL driver for accessing a screen. |
Nigel Tao | ad7d0ec | 2016-02-04 09:20:54 +1100 | [diff] [blame] | 6 | package gldriver // import "golang.org/x/exp/shiny/driver/gldriver" |
David Crawshaw | cabcb38 | 2015-07-30 14:52:09 -0400 | [diff] [blame] | 7 | |
| 8 | import ( |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 9 | "encoding/binary" |
| 10 | "fmt" |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 11 | "math" |
David Crawshaw | cabcb38 | 2015-07-30 14:52:09 -0400 | [diff] [blame] | 12 | |
Nigel Tao | ffc5391 | 2015-08-20 12:08:26 +1000 | [diff] [blame] | 13 | "golang.org/x/exp/shiny/driver/internal/errscreen" |
David Crawshaw | cabcb38 | 2015-07-30 14:52:09 -0400 | [diff] [blame] | 14 | "golang.org/x/exp/shiny/screen" |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 15 | "golang.org/x/image/math/f64" |
| 16 | "golang.org/x/mobile/gl" |
David Crawshaw | cabcb38 | 2015-07-30 14:52:09 -0400 | [diff] [blame] | 17 | ) |
| 18 | |
| 19 | // Main is called by the program's main function to run the graphical |
| 20 | // application. |
| 21 | // |
| 22 | // It calls f on the Screen, possibly in a separate goroutine, as some OS- |
| 23 | // specific libraries require being on 'the main thread'. It returns when f |
| 24 | // returns. |
| 25 | func Main(f func(screen.Screen)) { |
| 26 | if err := main(f); err != nil { |
Nigel Tao | ffc5391 | 2015-08-20 12:08:26 +1000 | [diff] [blame] | 27 | f(errscreen.Stub(err)) |
David Crawshaw | cabcb38 | 2015-07-30 14:52:09 -0400 | [diff] [blame] | 28 | } |
| 29 | } |
| 30 | |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 31 | func mul(a, b f64.Aff3) f64.Aff3 { |
| 32 | return f64.Aff3{ |
| 33 | a[0]*b[0] + a[1]*b[3], |
| 34 | a[0]*b[1] + a[1]*b[4], |
| 35 | a[0]*b[2] + a[1]*b[5] + a[2], |
| 36 | |
| 37 | a[3]*b[0] + a[4]*b[3], |
| 38 | a[3]*b[1] + a[4]*b[4], |
| 39 | a[3]*b[2] + a[4]*b[5] + a[5], |
| 40 | } |
| 41 | } |
| 42 | |
David Crawshaw | b80da08 | 2015-09-23 22:43:36 -0400 | [diff] [blame] | 43 | // writeAff3 must only be called while holding windowImpl.glctxMu. |
David Crawshaw | 82ea2f5 | 2015-09-23 19:37:56 -0400 | [diff] [blame] | 44 | func writeAff3(glctx gl.Context, u gl.Uniform, a f64.Aff3) { |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 45 | var m [9]float32 |
| 46 | m[0*3+0] = float32(a[0*3+0]) |
| 47 | m[0*3+1] = float32(a[1*3+0]) |
| 48 | m[0*3+2] = 0 |
| 49 | m[1*3+0] = float32(a[0*3+1]) |
| 50 | m[1*3+1] = float32(a[1*3+1]) |
| 51 | m[1*3+2] = 0 |
| 52 | m[2*3+0] = float32(a[0*3+2]) |
| 53 | m[2*3+1] = float32(a[1*3+2]) |
| 54 | m[2*3+2] = 1 |
David Crawshaw | 82ea2f5 | 2015-09-23 19:37:56 -0400 | [diff] [blame] | 55 | glctx.UniformMatrix3fv(u, m[:]) |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 56 | } |
| 57 | |
| 58 | // f32Bytes returns the byte representation of float32 values in the given byte |
| 59 | // order. byteOrder must be either binary.BigEndian or binary.LittleEndian. |
| 60 | func f32Bytes(byteOrder binary.ByteOrder, values ...float32) []byte { |
| 61 | le := false |
| 62 | switch byteOrder { |
| 63 | case binary.BigEndian: |
| 64 | case binary.LittleEndian: |
| 65 | le = true |
| 66 | default: |
| 67 | panic(fmt.Sprintf("invalid byte order %v", byteOrder)) |
| 68 | } |
| 69 | |
| 70 | b := make([]byte, 4*len(values)) |
| 71 | for i, v := range values { |
| 72 | u := math.Float32bits(v) |
| 73 | if le { |
| 74 | b[4*i+0] = byte(u >> 0) |
| 75 | b[4*i+1] = byte(u >> 8) |
| 76 | b[4*i+2] = byte(u >> 16) |
| 77 | b[4*i+3] = byte(u >> 24) |
| 78 | } else { |
| 79 | b[4*i+0] = byte(u >> 24) |
| 80 | b[4*i+1] = byte(u >> 16) |
| 81 | b[4*i+2] = byte(u >> 8) |
| 82 | b[4*i+3] = byte(u >> 0) |
| 83 | } |
| 84 | } |
| 85 | return b |
| 86 | } |
| 87 | |
David Crawshaw | b80da08 | 2015-09-23 22:43:36 -0400 | [diff] [blame] | 88 | // compileProgram must only be called while holding windowImpl.glctxMu. |
David Crawshaw | 82ea2f5 | 2015-09-23 19:37:56 -0400 | [diff] [blame] | 89 | func compileProgram(glctx gl.Context, vSrc, fSrc string) (gl.Program, error) { |
| 90 | program := glctx.CreateProgram() |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 91 | if program.Value == 0 { |
| 92 | return gl.Program{}, fmt.Errorf("gldriver: no programs available") |
| 93 | } |
| 94 | |
David Crawshaw | 82ea2f5 | 2015-09-23 19:37:56 -0400 | [diff] [blame] | 95 | vertexShader, err := compileShader(glctx, gl.VERTEX_SHADER, vSrc) |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 96 | if err != nil { |
| 97 | return gl.Program{}, err |
| 98 | } |
David Crawshaw | 82ea2f5 | 2015-09-23 19:37:56 -0400 | [diff] [blame] | 99 | fragmentShader, err := compileShader(glctx, gl.FRAGMENT_SHADER, fSrc) |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 100 | if err != nil { |
David Crawshaw | 82ea2f5 | 2015-09-23 19:37:56 -0400 | [diff] [blame] | 101 | glctx.DeleteShader(vertexShader) |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 102 | return gl.Program{}, err |
| 103 | } |
| 104 | |
David Crawshaw | 82ea2f5 | 2015-09-23 19:37:56 -0400 | [diff] [blame] | 105 | glctx.AttachShader(program, vertexShader) |
| 106 | glctx.AttachShader(program, fragmentShader) |
| 107 | glctx.LinkProgram(program) |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 108 | |
| 109 | // Flag shaders for deletion when program is unlinked. |
David Crawshaw | 82ea2f5 | 2015-09-23 19:37:56 -0400 | [diff] [blame] | 110 | glctx.DeleteShader(vertexShader) |
| 111 | glctx.DeleteShader(fragmentShader) |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 112 | |
David Crawshaw | 82ea2f5 | 2015-09-23 19:37:56 -0400 | [diff] [blame] | 113 | if glctx.GetProgrami(program, gl.LINK_STATUS) == 0 { |
| 114 | defer glctx.DeleteProgram(program) |
| 115 | return gl.Program{}, fmt.Errorf("gldriver: program compile: %s", glctx.GetProgramInfoLog(program)) |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 116 | } |
| 117 | return program, nil |
| 118 | } |
| 119 | |
David Crawshaw | b80da08 | 2015-09-23 22:43:36 -0400 | [diff] [blame] | 120 | // compileShader must only be called while holding windowImpl.glctxMu. |
David Crawshaw | 82ea2f5 | 2015-09-23 19:37:56 -0400 | [diff] [blame] | 121 | func compileShader(glctx gl.Context, shaderType gl.Enum, src string) (gl.Shader, error) { |
| 122 | shader := glctx.CreateShader(shaderType) |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 123 | if shader.Value == 0 { |
| 124 | return gl.Shader{}, fmt.Errorf("gldriver: could not create shader (type %v)", shaderType) |
| 125 | } |
David Crawshaw | 82ea2f5 | 2015-09-23 19:37:56 -0400 | [diff] [blame] | 126 | glctx.ShaderSource(shader, src) |
| 127 | glctx.CompileShader(shader) |
| 128 | if glctx.GetShaderi(shader, gl.COMPILE_STATUS) == 0 { |
| 129 | defer glctx.DeleteShader(shader) |
| 130 | return gl.Shader{}, fmt.Errorf("gldriver: shader compile: %s", glctx.GetShaderInfoLog(shader)) |
David Crawshaw | 6d4148d | 2015-08-01 10:13:44 -0400 | [diff] [blame] | 131 | } |
| 132 | return shader, nil |
| 133 | } |