painter.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // Package gl provides a full Fyne render implementation using system OpenGL libraries.
  2. package gl
  3. import (
  4. "fmt"
  5. "image"
  6. "fyne.io/fyne/v2"
  7. "fyne.io/fyne/v2/internal/driver"
  8. "fyne.io/fyne/v2/theme"
  9. )
  10. func shaderSourceNamed(name string) ([]byte, []byte) {
  11. switch name {
  12. case "line":
  13. return shaderLineVert.StaticContent, shaderLineFrag.StaticContent
  14. case "line_es":
  15. return shaderLineesVert.StaticContent, shaderLineesFrag.StaticContent
  16. case "simple":
  17. return shaderSimpleVert.StaticContent, shaderSimpleFrag.StaticContent
  18. case "simple_es":
  19. return shaderSimpleesVert.StaticContent, shaderSimpleesFrag.StaticContent
  20. case "rectangle":
  21. return shaderRectangleVert.StaticContent, shaderRectangleFrag.StaticContent
  22. case "round_rectangle":
  23. return shaderRectangleVert.StaticContent, shaderRoundrectangleFrag.StaticContent
  24. case "rectangle_es":
  25. return shaderRectangleesVert.StaticContent, shaderRectangleesFrag.StaticContent
  26. case "round_rectangle_es":
  27. return shaderRectangleesVert.StaticContent, shaderRoundrectangleesFrag.StaticContent
  28. }
  29. return nil, nil
  30. }
  31. // Painter defines the functionality of our OpenGL based renderer
  32. type Painter interface {
  33. // Init tell a new painter to initialise, usually called after a context is available
  34. Init()
  35. // Capture requests that the specified canvas be drawn to an in-memory image
  36. Capture(fyne.Canvas) image.Image
  37. // Clear tells our painter to prepare a fresh paint
  38. Clear()
  39. // Free is used to indicate that a certain canvas object is no longer needed
  40. Free(fyne.CanvasObject)
  41. // Paint a single fyne.CanvasObject but not its children.
  42. Paint(fyne.CanvasObject, fyne.Position, fyne.Size)
  43. // SetFrameBufferScale tells us when we have more than 1 framebuffer pixel for each output pixel
  44. SetFrameBufferScale(float32)
  45. // SetOutputSize is used to change the resolution of our output viewport
  46. SetOutputSize(int, int)
  47. // StartClipping tells us that the following paint actions should be clipped to the specified area.
  48. StartClipping(fyne.Position, fyne.Size)
  49. // StopClipping stops clipping paint actions.
  50. StopClipping()
  51. }
  52. // NewPainter creates a new GL based renderer for the provided canvas.
  53. // If it is a master painter it will also initialise OpenGL
  54. func NewPainter(c fyne.Canvas, ctx driver.WithContext) Painter {
  55. p := &painter{canvas: c, contextProvider: ctx}
  56. p.SetFrameBufferScale(1.0)
  57. return p
  58. }
  59. type painter struct {
  60. canvas fyne.Canvas
  61. ctx context
  62. contextProvider driver.WithContext
  63. program Program
  64. lineProgram Program
  65. rectangleProgram Program
  66. roundRectangleProgram Program
  67. texScale float32
  68. pixScale float32 // pre-calculate scale*texScale for each draw
  69. }
  70. // Declare conformity to Painter interface
  71. var _ Painter = (*painter)(nil)
  72. func (p *painter) Clear() {
  73. r, g, b, a := theme.BackgroundColor().RGBA()
  74. p.ctx.ClearColor(float32(r)/max16bit, float32(g)/max16bit, float32(b)/max16bit, float32(a)/max16bit)
  75. p.ctx.Clear(bitColorBuffer | bitDepthBuffer)
  76. p.logError()
  77. }
  78. func (p *painter) Free(obj fyne.CanvasObject) {
  79. p.freeTexture(obj)
  80. }
  81. func (p *painter) Paint(obj fyne.CanvasObject, pos fyne.Position, frame fyne.Size) {
  82. if obj.Visible() {
  83. p.drawObject(obj, pos, frame)
  84. }
  85. }
  86. func (p *painter) SetFrameBufferScale(scale float32) {
  87. p.texScale = scale
  88. p.pixScale = p.canvas.Scale() * p.texScale
  89. }
  90. func (p *painter) SetOutputSize(width, height int) {
  91. p.ctx.Viewport(0, 0, width, height)
  92. p.logError()
  93. }
  94. func (p *painter) StartClipping(pos fyne.Position, size fyne.Size) {
  95. x := p.textureScale(pos.X)
  96. y := p.textureScale(p.canvas.Size().Height - pos.Y - size.Height)
  97. w := p.textureScale(size.Width)
  98. h := p.textureScale(size.Height)
  99. p.ctx.Scissor(int32(x), int32(y), int32(w), int32(h))
  100. p.ctx.Enable(scissorTest)
  101. p.logError()
  102. }
  103. func (p *painter) StopClipping() {
  104. p.ctx.Disable(scissorTest)
  105. p.logError()
  106. }
  107. func (p *painter) compileShader(source string, shaderType uint32) (Shader, error) {
  108. shader := p.ctx.CreateShader(shaderType)
  109. p.ctx.ShaderSource(shader, source)
  110. p.logError()
  111. p.ctx.CompileShader(shader)
  112. p.logError()
  113. info := p.ctx.GetShaderInfoLog(shader)
  114. if p.ctx.GetShaderi(shader, compileStatus) == glFalse {
  115. return noShader, fmt.Errorf("failed to compile OpenGL shader:\n%s\n>>> SHADER SOURCE\n%s\n<<< SHADER SOURCE", info, source)
  116. }
  117. // The info is probably a null terminated string.
  118. // An empty info has been seen as "\x00" or "\x00\x00".
  119. if len(info) > 0 && info != "\x00" && info != "\x00\x00" {
  120. fmt.Printf("OpenGL shader compilation output:\n%s\n>>> SHADER SOURCE\n%s\n<<< SHADER SOURCE\n", info, source)
  121. }
  122. return shader, nil
  123. }
  124. func (p *painter) createProgram(shaderFilename string) Program {
  125. // Why a switch over a filename?
  126. // Because this allows for a minimal change, once we reach Go 1.16 and use go:embed instead of
  127. // fyne bundle.
  128. vertexSrc, fragmentSrc := shaderSourceNamed(shaderFilename)
  129. if vertexSrc == nil {
  130. panic("shader not found: " + shaderFilename)
  131. }
  132. vertShader, err := p.compileShader(string(vertexSrc), vertexShader)
  133. if err != nil {
  134. panic(err)
  135. }
  136. fragShader, err := p.compileShader(string(fragmentSrc), fragmentShader)
  137. if err != nil {
  138. panic(err)
  139. }
  140. prog := p.ctx.CreateProgram()
  141. p.ctx.AttachShader(prog, vertShader)
  142. p.ctx.AttachShader(prog, fragShader)
  143. p.ctx.LinkProgram(prog)
  144. info := p.ctx.GetProgramInfoLog(prog)
  145. if p.ctx.GetProgrami(prog, linkStatus) == glFalse {
  146. panic(fmt.Errorf("failed to link OpenGL program:\n%s", info))
  147. }
  148. // The info is probably a null terminated string.
  149. // An empty info has been seen as "\x00" or "\x00\x00".
  150. if len(info) > 0 && info != "\x00" && info != "\x00\x00" {
  151. fmt.Printf("OpenGL program linking output:\n%s\n", info)
  152. }
  153. if glErr := p.ctx.GetError(); glErr != 0 {
  154. panic(fmt.Sprintf("failed to link OpenGL program; error code: %x", glErr))
  155. }
  156. return prog
  157. }
  158. func (p *painter) logError() {
  159. logGLError(p.ctx.GetError)
  160. }