| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- // Package gl provides a full Fyne render implementation using system OpenGL libraries.
- package gl
- import (
- "fmt"
- "image"
- "fyne.io/fyne/v2"
- "fyne.io/fyne/v2/internal/driver"
- "fyne.io/fyne/v2/theme"
- )
- func shaderSourceNamed(name string) ([]byte, []byte) {
- switch name {
- case "line":
- return shaderLineVert.StaticContent, shaderLineFrag.StaticContent
- case "line_es":
- return shaderLineesVert.StaticContent, shaderLineesFrag.StaticContent
- case "simple":
- return shaderSimpleVert.StaticContent, shaderSimpleFrag.StaticContent
- case "simple_es":
- return shaderSimpleesVert.StaticContent, shaderSimpleesFrag.StaticContent
- case "single_channel":
- return shaderSimpleVert.StaticContent, shaderSinglechannelFrag.StaticContent
- case "single_channel_es":
- return shaderSimpleesVert.StaticContent, shaderSinglechannelesFrag.StaticContent
- }
- return nil, nil
- }
- // Painter defines the functionality of our OpenGL based renderer
- type Painter interface {
- // Init tell a new painter to initialise, usually called after a context is available
- Init()
- // Capture requests that the specified canvas be drawn to an in-memory image
- Capture(fyne.Canvas) image.Image
- // Clear tells our painter to prepare a fresh paint
- Clear()
- // Free is used to indicate that a certain canvas object is no longer needed
- Free(fyne.CanvasObject)
- // Paint a single fyne.CanvasObject but not its children.
- Paint(fyne.CanvasObject, fyne.Position, fyne.Size)
- // SetFrameBufferScale tells us when we have more than 1 framebuffer pixel for each output pixel
- SetFrameBufferScale(float32)
- // SetOutputSize is used to change the resolution of our output viewport
- SetOutputSize(int, int)
- // StartClipping tells us that the following paint actions should be clipped to the specified area.
- StartClipping(fyne.Position, fyne.Size)
- // StopClipping stops clipping paint actions.
- StopClipping()
- }
- // NewPainter creates a new GL based renderer for the provided canvas.
- // If it is a master painter it will also initialise OpenGL
- func NewPainter(c fyne.Canvas, ctx driver.WithContext) Painter {
- p := &painter{canvas: c, contextProvider: ctx}
- p.SetFrameBufferScale(1.0)
- return p
- }
- type painter struct {
- canvas fyne.Canvas
- ctx context
- contextProvider driver.WithContext
- program Program
- singleChannelProgram Program
- lineProgram Program
- texScale float32
- pixScale float32 // pre-calculate scale*texScale for each draw
- }
- // Declare conformity to Painter interface
- var _ Painter = (*painter)(nil)
- func (p *painter) Clear() {
- r, g, b, a := theme.BackgroundColor().RGBA()
- p.ctx.ClearColor(float32(r)/max16bit, float32(g)/max16bit, float32(b)/max16bit, float32(a)/max16bit)
- p.ctx.Clear(bitColorBuffer | bitDepthBuffer)
- p.logError()
- }
- func (p *painter) Free(obj fyne.CanvasObject) {
- p.freeTexture(obj)
- }
- func (p *painter) Paint(obj fyne.CanvasObject, pos fyne.Position, frame fyne.Size) {
- if obj.Visible() {
- p.drawObject(obj, pos, frame)
- }
- }
- func (p *painter) SetFrameBufferScale(scale float32) {
- p.texScale = scale
- p.pixScale = p.canvas.Scale() * p.texScale
- }
- func (p *painter) SetOutputSize(width, height int) {
- p.ctx.Viewport(0, 0, width, height)
- p.logError()
- }
- func (p *painter) StartClipping(pos fyne.Position, size fyne.Size) {
- x := p.textureScale(pos.X)
- y := p.textureScale(p.canvas.Size().Height - pos.Y - size.Height)
- w := p.textureScale(size.Width)
- h := p.textureScale(size.Height)
- p.ctx.Scissor(int32(x), int32(y), int32(w), int32(h))
- p.ctx.Enable(scissorTest)
- p.logError()
- }
- func (p *painter) StopClipping() {
- p.ctx.Disable(scissorTest)
- p.logError()
- }
- func (p *painter) compileShader(source string, shaderType uint32) (Shader, error) {
- shader := p.ctx.CreateShader(shaderType)
- p.ctx.ShaderSource(shader, source)
- p.logError()
- p.ctx.CompileShader(shader)
- p.logError()
- info := p.ctx.GetShaderInfoLog(shader)
- if p.ctx.GetShaderi(shader, compileStatus) == glFalse {
- return noShader, fmt.Errorf("failed to compile OpenGL shader:\n%s\n>>> SHADER SOURCE\n%s\n<<< SHADER SOURCE", info, source)
- }
- // The info is probably a null terminated string.
- // An empty info has been seen as "\x00" or "\x00\x00".
- if len(info) > 0 && info != "\x00" && info != "\x00\x00" {
- fmt.Printf("OpenGL shader compilation output:\n%s\n>>> SHADER SOURCE\n%s\n<<< SHADER SOURCE\n", info, source)
- }
- return shader, nil
- }
- func (p *painter) createProgram(shaderFilename string) Program {
- // Why a switch over a filename?
- // Because this allows for a minimal change, once we reach Go 1.16 and use go:embed instead of
- // fyne bundle.
- vertexSrc, fragmentSrc := shaderSourceNamed(shaderFilename)
- if vertexSrc == nil {
- panic("shader not found: " + shaderFilename)
- }
- vertShader, err := p.compileShader(string(vertexSrc), vertexShader)
- if err != nil {
- panic(err)
- }
- fragShader, err := p.compileShader(string(fragmentSrc), fragmentShader)
- if err != nil {
- panic(err)
- }
- prog := p.ctx.CreateProgram()
- p.ctx.AttachShader(prog, vertShader)
- p.ctx.AttachShader(prog, fragShader)
- p.ctx.LinkProgram(prog)
- info := p.ctx.GetProgramInfoLog(prog)
- if p.ctx.GetProgrami(prog, linkStatus) == glFalse {
- panic(fmt.Errorf("failed to link OpenGL program:\n%s", info))
- }
- // The info is probably a null terminated string.
- // An empty info has been seen as "\x00" or "\x00\x00".
- if len(info) > 0 && info != "\x00" && info != "\x00\x00" {
- fmt.Printf("OpenGL program linking output:\n%s\n", info)
- }
- if glErr := p.ctx.GetError(); glErr != 0 {
- panic(fmt.Sprintf("failed to link OpenGL program; error code: %x", glErr))
- }
- return prog
- }
- func (p *painter) logError() {
- logGLError(p.ctx.GetError())
- }
|