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  }