fps.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. // Copyright 2014 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. //go:build darwin || linux || windows
  5. // Package debug provides GL-based debugging tools for apps.
  6. package debug
  7. import (
  8. "image"
  9. "image/color"
  10. "image/draw"
  11. "time"
  12. "golang.org/x/mobile/event/size"
  13. "golang.org/x/mobile/exp/gl/glutil"
  14. "golang.org/x/mobile/geom"
  15. )
  16. // FPS draws a count of the frames rendered per second.
  17. type FPS struct {
  18. sz size.Event
  19. images *glutil.Images
  20. m *glutil.Image
  21. lastDraw time.Time
  22. // TODO: store *gl.Context
  23. }
  24. // NewFPS creates an FPS tied to the current GL context.
  25. func NewFPS(images *glutil.Images) *FPS {
  26. return &FPS{
  27. lastDraw: time.Now(),
  28. images: images,
  29. }
  30. }
  31. // Draw draws the per second framerate in the bottom-left of the screen.
  32. func (p *FPS) Draw(sz size.Event) {
  33. const imgW, imgH = 7*(fontWidth+1) + 1, fontHeight + 2
  34. if sz.WidthPx == 0 && sz.HeightPx == 0 {
  35. return
  36. }
  37. if p.sz != sz {
  38. p.sz = sz
  39. if p.m != nil {
  40. p.m.Release()
  41. }
  42. p.m = p.images.NewImage(imgW, imgH)
  43. }
  44. display := [7]byte{
  45. 4: 'F',
  46. 5: 'P',
  47. 6: 'S',
  48. }
  49. now := time.Now()
  50. f := 0
  51. if dur := now.Sub(p.lastDraw); dur > 0 {
  52. f = int(time.Second / dur)
  53. }
  54. display[2] = '0' + byte((f/1e0)%10)
  55. display[1] = '0' + byte((f/1e1)%10)
  56. display[0] = '0' + byte((f/1e2)%10)
  57. draw.Draw(p.m.RGBA, p.m.RGBA.Bounds(), image.White, image.Point{}, draw.Src)
  58. for i, c := range display {
  59. glyph := glyphs[c]
  60. if len(glyph) != fontWidth*fontHeight {
  61. continue
  62. }
  63. for y := 0; y < fontHeight; y++ {
  64. for x := 0; x < fontWidth; x++ {
  65. if glyph[fontWidth*y+x] == ' ' {
  66. continue
  67. }
  68. p.m.RGBA.SetRGBA((fontWidth+1)*i+x+1, y+1, color.RGBA{A: 0xff})
  69. }
  70. }
  71. }
  72. p.m.Upload()
  73. p.m.Draw(
  74. sz,
  75. geom.Point{0, sz.HeightPt - imgH},
  76. geom.Point{imgW, sz.HeightPt - imgH},
  77. geom.Point{0, sz.HeightPt},
  78. p.m.RGBA.Bounds(),
  79. )
  80. p.lastDraw = now
  81. }
  82. func (f *FPS) Release() {
  83. if f.m != nil {
  84. f.m.Release()
  85. f.m = nil
  86. f.images = nil
  87. }
  88. }
  89. const (
  90. fontWidth = 5
  91. fontHeight = 7
  92. )
  93. // glyphs comes from the 6x10 fixed font from the plan9port:
  94. // https://github.com/9fans/plan9port/tree/master/font/fixed
  95. //
  96. // 6x10 becomes 5x7 because each glyph has a 1-pixel margin plus space for
  97. // descenders.
  98. //
  99. // Its README file says that those fonts were converted from XFree86, and are
  100. // in the public domain.
  101. var glyphs = [256]string{
  102. '0': "" +
  103. " X " +
  104. " X X " +
  105. "X X" +
  106. "X X" +
  107. "X X" +
  108. " X X " +
  109. " X ",
  110. '1': "" +
  111. " X " +
  112. " XX " +
  113. "X X " +
  114. " X " +
  115. " X " +
  116. " X " +
  117. "XXXXX",
  118. '2': "" +
  119. " XXX " +
  120. "X X" +
  121. " X" +
  122. " XX " +
  123. " X " +
  124. "X " +
  125. "XXXXX",
  126. '3': "" +
  127. "XXXXX" +
  128. " X" +
  129. " X " +
  130. " XX " +
  131. " X" +
  132. "X X" +
  133. " XXX ",
  134. '4': "" +
  135. " X " +
  136. " XX " +
  137. " X X " +
  138. "X X " +
  139. "XXXXX" +
  140. " X " +
  141. " X ",
  142. '5': "" +
  143. "XXXXX" +
  144. "X " +
  145. "X XX " +
  146. "XX X" +
  147. " X" +
  148. "X X" +
  149. " XXX ",
  150. '6': "" +
  151. " XX " +
  152. " X " +
  153. "X " +
  154. "X XX " +
  155. "XX X" +
  156. "X X" +
  157. " XXX ",
  158. '7': "" +
  159. "XXXXX" +
  160. " X" +
  161. " X " +
  162. " X " +
  163. " X " +
  164. " X " +
  165. " X ",
  166. '8': "" +
  167. " XXX " +
  168. "X X" +
  169. "X X" +
  170. " XXX " +
  171. "X X" +
  172. "X X" +
  173. " XXX ",
  174. '9': "" +
  175. " XXX " +
  176. "X X" +
  177. "X XX" +
  178. " XX X" +
  179. " X" +
  180. " X " +
  181. " XX ",
  182. 'F': "" +
  183. "XXXXX" +
  184. "X " +
  185. "X " +
  186. "XXXX " +
  187. "X " +
  188. "X " +
  189. "X ",
  190. 'P': "" +
  191. "XXXX " +
  192. "X X" +
  193. "X X" +
  194. "XXXX " +
  195. "X " +
  196. "X " +
  197. "X ",
  198. 'S': "" +
  199. " XXX " +
  200. "X X" +
  201. "X " +
  202. " XXX " +
  203. " X" +
  204. "X X" +
  205. " XXX ",
  206. }