program.go 5.8 KB

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