You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: opengl.go
    path of file: ./repos/gdlv/vendor/gioui.org/gpu/internal/opengl
    
    the code of the file until where you have to start completion: // SPDX-License-Identifier: Unlicense OR MIT

package opengl

import (
	"errors"
	"fmt"
	"image"
	"math/bits"
	"runtime"
	"strings"
	"time"
	"unsafe"

	"gioui.org/gpu/internal/driver"
	"gioui.org/internal/gl"
	"gioui.org/shader"
)

// Backend implements driver.Device.
type Backend struct {
	funcs *gl.Functions

	clear      bool
	glstate    glState
	state      state
	savedState glState
	sharedCtx  bool

	glver [2]int
	gles  bool
	feats driver.Caps
	// floatTriple holds the settings for floating point
	// textures.
	floatTriple textureTriple
	// Single channel alpha textures.
	alphaTriple textureTriple
	srgbaTriple textureTriple
	storage     [storageBindings]*buffer

	outputFBO gl.Framebuffer
	sRGBFBO   *SRGBFBO

	// vertArray is bound during a frame. We don't need it, but
	// core desktop OpenGL profile 3.3 requires some array bound.
	vertArray gl.VertexArray
}

// State tracking.
type glState struct {
	drawFBO     gl.Framebuffer
	readFBO     gl.Framebu
    
func (p *uniforms) update(funcs *gl.Functions, buf *buffer) {
	if buf.size < p.size {
		panic(fmt.Errorf("uniform buffer too small, got %d need %d", buf.size, p.size))
	}
	data := buf.data
	for _, u := range p.locs {
		if !u.uniform.Valid() {
			continue
		}
		data := data[u.offset:]
		switch {
		case u.typ == shader.DataTypeFloat && u.size == 1:
			data := data[:4]
			v := *(*[1]float32)(unsafe.Pointer(&data[0]))
			funcs.Uniform1f(u.uniform, v[0])
		case u.typ == shader.DataTypeFloat && u.size == 2:
			data := data[:8]
			v := *(*[2]float32)(unsafe.Pointer(&data[0]))
			funcs.Uniform2f(u.uniform, v[0], v[1])
		case u.typ == shader.DataTypeFloat && u.size == 3:
			data := data[:12]
			v := *(*[3]float32)(unsafe.Pointer(&data[0]))
			funcs.Uniform3f(u.uniform, v[0], v[1], v[2])
		case u.typ == shader.DataTypeFloat && u.size == 4:
			data := data[:16]
			v := *(*[4]float32)(unsafe.Pointer(&data[0]))
			funcs.Uniform4f(u.uniform, v[0], v[1], v[2], v[3])
		default:
			panic("unsupported uniform data type or size")
		}
	}
}