gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/app/internal/gpu/context.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package gpu 4 5 import ( 6 "errors" 7 "strings" 8 9 "gioui.org/ui/app/internal/gl" 10 ) 11 12 type context struct { 13 caps caps 14 *gl.Functions 15 } 16 17 type caps struct { 18 EXT_disjoint_timer_query bool 19 // floatTriple holds the settings for floating point 20 // textures. 21 floatTriple textureTriple 22 // Single channel alpha textures. 23 alphaTriple textureTriple 24 srgbaTriple textureTriple 25 } 26 27 // textureTriple holds the type settings for 28 // a TexImage2D call. 29 type textureTriple struct { 30 internalFormat int 31 format gl.Enum 32 typ gl.Enum 33 } 34 35 func newContext(glctx gl.Context) (*context, error) { 36 ctx := &context{ 37 Functions: glctx.Functions(), 38 } 39 exts := strings.Split(ctx.GetString(gl.EXTENSIONS), " ") 40 glVer := ctx.GetString(gl.VERSION) 41 ver, err := gl.ParseGLVersion(glVer) 42 if err != nil { 43 return nil, err 44 } 45 floatTriple, err := floatTripleFor(ctx, ver, exts) 46 if err != nil { 47 return nil, err 48 } 49 srgbaTriple, err := srgbaTripleFor(ver, exts) 50 if err != nil { 51 return nil, err 52 } 53 hasTimers := hasExtension(exts, "GL_EXT_disjoint_timer_query_webgl2") || hasExtension(exts, "GL_EXT_disjoint_timer_query") 54 ctx.caps = caps{ 55 EXT_disjoint_timer_query: hasTimers, 56 floatTriple: floatTriple, 57 alphaTriple: alphaTripleFor(ver), 58 srgbaTriple: srgbaTriple, 59 } 60 return ctx, nil 61 } 62 63 // floatTripleFor determines the best texture triple for floating point FBOs. 64 func floatTripleFor(ctx *context, ver [2]int, exts []string) (textureTriple, error) { 65 var triples []textureTriple 66 if ver[0] >= 3 { 67 triples = append(triples, textureTriple{gl.R16F, gl.Enum(gl.RED), gl.Enum(gl.HALF_FLOAT)}) 68 } 69 if hasExtension(exts, "GL_OES_texture_half_float") || hasExtension(exts, "EXT_color_buffer_half_float") { 70 triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.HALF_FLOAT_OES)}) 71 } 72 if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") { 73 triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.FLOAT)}) 74 } 75 tex := ctx.CreateTexture() 76 defer ctx.DeleteTexture(tex) 77 ctx.BindTexture(gl.TEXTURE_2D, tex) 78 ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) 79 ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) 80 ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) 81 ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) 82 fbo := ctx.CreateFramebuffer() 83 defer ctx.DeleteFramebuffer(fbo) 84 defFBO := gl.Framebuffer(ctx.GetBinding(gl.FRAMEBUFFER_BINDING)) 85 ctx.BindFramebuffer(gl.FRAMEBUFFER, fbo) 86 defer ctx.BindFramebuffer(gl.FRAMEBUFFER, defFBO) 87 for _, tt := range triples { 88 const size = 256 89 ctx.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ, nil) 90 ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0) 91 if st := ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); st == gl.FRAMEBUFFER_COMPLETE { 92 return tt, nil 93 } 94 } 95 return textureTriple{}, errors.New("floating point fbos not supported") 96 } 97 98 func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) { 99 switch { 100 case ver[0] >= 3: 101 return textureTriple{gl.SRGB8_ALPHA8, gl.Enum(gl.RGBA), gl.Enum(gl.UNSIGNED_BYTE)}, nil 102 case hasExtension(exts, "GL_EXT_sRGB"): 103 return textureTriple{gl.SRGB_ALPHA_EXT, gl.Enum(gl.SRGB_ALPHA_EXT), gl.Enum(gl.UNSIGNED_BYTE)}, nil 104 default: 105 return textureTriple{}, errors.New("no sRGB texture formats found") 106 } 107 } 108 109 func alphaTripleFor(ver [2]int) textureTriple { 110 intf, f := gl.R8, gl.Enum(gl.RED) 111 if ver[0] < 3 { 112 // R8, RED not supported on OpenGL ES 2.0. 113 intf, f = gl.LUMINANCE, gl.Enum(gl.LUMINANCE) 114 } 115 return textureTriple{intf, f, gl.UNSIGNED_BYTE} 116 } 117 118 func hasExtension(exts []string, ext string) bool { 119 for _, e := range exts { 120 if ext == e { 121 return true 122 } 123 } 124 return false 125 }