github.com/gop9/olt@v0.0.0-20200202132135-d956aad50b08/gio/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 "github.com/gop9/olt/gio/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.Functions) (*context, error) { 36 ctx := &context{ 37 Functions: glctx, 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, "GL_EXT_color_buffer_half_float") { 70 // Try single channel. 71 triples = append(triples, textureTriple{gl.LUMINANCE, gl.Enum(gl.LUMINANCE), gl.Enum(gl.HALF_FLOAT_OES)}) 72 // Fallback to 4 channels. 73 triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.HALF_FLOAT_OES)}) 74 } 75 if hasExtension(exts, "GL_OES_texture_float") || hasExtension(exts, "GL_EXT_color_buffer_float") { 76 triples = append(triples, textureTriple{gl.RGBA, gl.Enum(gl.RGBA), gl.Enum(gl.FLOAT)}) 77 } 78 tex := ctx.CreateTexture() 79 defer ctx.DeleteTexture(tex) 80 ctx.BindTexture(gl.TEXTURE_2D, tex) 81 ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) 82 ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) 83 ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) 84 ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) 85 fbo := ctx.CreateFramebuffer() 86 defer ctx.DeleteFramebuffer(fbo) 87 defFBO := gl.Framebuffer(ctx.GetBinding(gl.FRAMEBUFFER_BINDING)) 88 ctx.BindFramebuffer(gl.FRAMEBUFFER, fbo) 89 defer ctx.BindFramebuffer(gl.FRAMEBUFFER, defFBO) 90 for _, tt := range triples { 91 const size = 256 92 ctx.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, size, size, tt.format, tt.typ, nil) 93 ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0) 94 if st := ctx.CheckFramebufferStatus(gl.FRAMEBUFFER); st == gl.FRAMEBUFFER_COMPLETE { 95 return tt, nil 96 } 97 } 98 return textureTriple{}, errors.New("floating point fbos not supported") 99 } 100 101 func srgbaTripleFor(ver [2]int, exts []string) (textureTriple, error) { 102 switch { 103 case ver[0] >= 3: 104 return textureTriple{gl.SRGB8_ALPHA8, gl.Enum(gl.RGBA), gl.Enum(gl.UNSIGNED_BYTE)}, nil 105 case hasExtension(exts, "GL_EXT_sRGB"): 106 return textureTriple{gl.SRGB_ALPHA_EXT, gl.Enum(gl.SRGB_ALPHA_EXT), gl.Enum(gl.UNSIGNED_BYTE)}, nil 107 default: 108 return textureTriple{}, errors.New("no sRGB texture formats found") 109 } 110 } 111 112 func alphaTripleFor(ver [2]int) textureTriple { 113 intf, f := gl.R8, gl.Enum(gl.RED) 114 if ver[0] < 3 { 115 // R8, RED not supported on OpenGL ES 2.0. 116 intf, f = gl.LUMINANCE, gl.Enum(gl.LUMINANCE) 117 } 118 return textureTriple{intf, f, gl.UNSIGNED_BYTE} 119 } 120 121 func hasExtension(exts []string, ext string) bool { 122 for _, e := range exts { 123 if ext == e { 124 return true 125 } 126 } 127 return false 128 }