program.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright 2016 The G3N 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. package gls
  5. import (
  6. "bytes"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "strconv"
  11. "strings"
  12. )
  13. // Program represents an OpenGL program.
  14. // It must have Vertex and Fragment shaders.
  15. // It can also have a Geometry shader.
  16. type Program struct {
  17. gs *GLS // OpenGL state
  18. ShowSource bool // Show source code in error messages
  19. handle uint32 // OpenGL program handle
  20. shaders []shaderInfo // List of shaders for this program
  21. uniforms map[string]int32 // List of uniforms
  22. }
  23. // shaderInfo contains OpenGL-related shader information.
  24. type shaderInfo struct {
  25. stype uint32 // OpenGL shader type (VERTEX_SHADER, FRAGMENT_SHADER, or GEOMETRY_SHADER)
  26. source string // Shader source code
  27. handle uint32 // OpenGL shader handle
  28. }
  29. // Map from shader types to names.
  30. var shaderNames = map[uint32]string{
  31. VERTEX_SHADER: "Vertex Shader",
  32. FRAGMENT_SHADER: "Fragment Shader",
  33. GEOMETRY_SHADER: "Geometry Shader",
  34. }
  35. // NewProgram creates and returns a new empty shader program object.
  36. // Use this type methods to add shaders and build the final program.
  37. func (gs *GLS) NewProgram() *Program {
  38. prog := new(Program)
  39. prog.gs = gs
  40. prog.shaders = make([]shaderInfo, 0)
  41. prog.uniforms = make(map[string]int32)
  42. prog.ShowSource = true
  43. return prog
  44. }
  45. // Handle returns the OpenGL handle of this program.
  46. func (prog *Program) Handle() uint32 {
  47. return prog.handle
  48. }
  49. // AddShader adds a shader to this program.
  50. // This must be done before the program is built.
  51. func (prog *Program) AddShader(stype uint32, source string) {
  52. // Check if program already built
  53. if prog.handle != 0 {
  54. log.Fatal("Program already built")
  55. }
  56. prog.shaders = append(prog.shaders, shaderInfo{stype, source, 0})
  57. }
  58. // DeleteShaders deletes all of this program's shaders from OpenGL.
  59. func (prog *Program) DeleteShaders() {
  60. for _, shaderInfo := range prog.shaders {
  61. if shaderInfo.handle != 0 {
  62. prog.gs.DeleteShader(shaderInfo.handle)
  63. shaderInfo.handle = 0
  64. }
  65. }
  66. }
  67. // Build builds the program, compiling and linking the previously supplied shaders.
  68. func (prog *Program) Build() error {
  69. // Check if program already built
  70. if prog.handle != 0 {
  71. return fmt.Errorf("program already built")
  72. }
  73. // Check if shaders were provided
  74. if len(prog.shaders) == 0 {
  75. return fmt.Errorf("no shaders supplied")
  76. }
  77. // Create program
  78. prog.handle = prog.gs.CreateProgram()
  79. if prog.handle == 0 {
  80. return fmt.Errorf("error creating program")
  81. }
  82. // Clean unused GL allocated resources
  83. defer prog.DeleteShaders()
  84. // Compile and attach shaders
  85. for _, sinfo := range prog.shaders {
  86. shader, err := prog.CompileShader(sinfo.stype, sinfo.source)
  87. if err != nil {
  88. prog.gs.DeleteProgram(prog.handle)
  89. prog.handle = 0
  90. msg := fmt.Sprintf("error compiling %s: %s", shaderNames[sinfo.stype], err)
  91. if prog.ShowSource {
  92. msg += FormatSource(sinfo.source)
  93. }
  94. return errors.New(msg)
  95. }
  96. sinfo.handle = shader
  97. prog.gs.AttachShader(prog.handle, shader)
  98. }
  99. // Link program and check for errors
  100. prog.gs.LinkProgram(prog.handle)
  101. var status int32
  102. prog.gs.GetProgramiv(prog.handle, LINK_STATUS, &status)
  103. if status == FALSE {
  104. log := prog.gs.GetProgramInfoLog(prog.handle)
  105. prog.handle = 0
  106. return fmt.Errorf("error linking program: %v", log)
  107. }
  108. return nil
  109. }
  110. // GetAttribLocation returns the location of the specified attribute
  111. // in this program. This location is internally cached.
  112. func (prog *Program) GetAttribLocation(name string) int32 {
  113. return prog.gs.GetAttribLocation(prog.handle, name)
  114. }
  115. // GetUniformLocation returns the location of the specified uniform in this program.
  116. // This location is internally cached.
  117. func (prog *Program) GetUniformLocation(name string) int32 {
  118. // Try to get from the cache
  119. loc, ok := prog.uniforms[name]
  120. if ok {
  121. prog.gs.stats.UnilocHits++
  122. return loc
  123. }
  124. // Get location from OpenGL
  125. loc = prog.gs.GetUniformLocation(prog.handle, name)
  126. prog.gs.stats.UnilocMiss++
  127. // Cache result
  128. prog.uniforms[name] = loc
  129. if loc < 0 {
  130. log.Warn("Program.GetUniformLocation(%s): NOT FOUND", name)
  131. }
  132. return loc
  133. }
  134. // CompileShader creates and compiles an OpenGL shader of the specified type, with
  135. // the specified source code, and returns a non-zero value by which it can be referenced.
  136. func (prog *Program) CompileShader(stype uint32, source string) (uint32, error) {
  137. // Create shader object
  138. shader := prog.gs.CreateShader(stype)
  139. if shader == 0 {
  140. return 0, fmt.Errorf("error creating shader")
  141. }
  142. // Set shader source and compile it
  143. prog.gs.ShaderSource(shader, source)
  144. prog.gs.CompileShader(shader)
  145. // Get the shader compiler log
  146. slog := prog.gs.GetShaderInfoLog(shader)
  147. // Get the shader compile status
  148. var status int32
  149. prog.gs.GetShaderiv(shader, COMPILE_STATUS, &status)
  150. if status == FALSE {
  151. return shader, fmt.Errorf("%s", slog)
  152. }
  153. // If the shader compiled OK but the log has data,
  154. // log this data instead of returning error
  155. if len(slog) > 2 {
  156. log.Warn("%s", slog)
  157. }
  158. return shader, nil
  159. }
  160. // FormatSource returns the supplied program source code with
  161. // line numbers prepended.
  162. func FormatSource(source string) string {
  163. // Reads all lines from the source string
  164. lines := make([]string, 0)
  165. buf := bytes.NewBuffer([]byte(source))
  166. for {
  167. line, err := buf.ReadBytes('\n')
  168. if err != nil {
  169. if err == io.EOF {
  170. break
  171. }
  172. panic(err)
  173. }
  174. lines = append(lines, string(line[:len(line)-1]))
  175. }
  176. // Adds a final line terminator
  177. lines = append(lines, "\n")
  178. // Prepends the line number for each line
  179. ndigits := len(strconv.Itoa(len(lines)))
  180. format := "%0" + strconv.Itoa(ndigits) + "d:%s"
  181. formatted := make([]string, 0)
  182. for pos, l := range lines {
  183. fline := fmt.Sprintf(format, pos+1, l)
  184. formatted = append(formatted, fline)
  185. }
  186. return strings.Join(formatted, "\n")
  187. }