gpu,gpu/gl: introduce Backend

A recent change made the OpenGL functions an interface of the functions
required for the implementation of GPU, a renderer for Gio operations.
That allowed for running Gio on external systems where OpenGL is
available.

However, to allow for non-OpenGL flavored backends such as Vulkan,
Metal and Direct3D, this change introduces Backend for the high-level
operations required by GPU. This change also adds a concrete backend
to package gl.

Type Backend is a first cut heavily based on OpenGL. Future changes will add
more backends, where the Backend interface quite possibly will need refinement.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2020-02-09 17:03:57 +01:00
parent 9602337b45
commit 3ae5a37c24
16 changed files with 984 additions and 483 deletions
+106 -132
View File
@@ -10,12 +10,12 @@ import (
"unsafe"
"gioui.org/f32"
"gioui.org/gpu/gl"
"gioui.org/internal/path"
gunsafe "gioui.org/internal/unsafe"
)
type pather struct {
ctx *context
ctx Backend
viewport image.Point
@@ -24,30 +24,30 @@ type pather struct {
}
type coverer struct {
ctx *context
prog [2]gl.Program
ctx Backend
prog [2]Program
vars [2]struct {
z gl.Uniform
uScale, uOffset gl.Uniform
uUVScale, uUVOffset gl.Uniform
uCoverUVScale, uCoverUVOffset gl.Uniform
uColor gl.Uniform
z Uniform
uScale, uOffset Uniform
uUVScale, uUVOffset Uniform
uCoverUVScale, uCoverUVOffset Uniform
uColor Uniform
}
}
type stenciler struct {
ctx *context
defFBO gl.Framebuffer
ctx Backend
defFBO Framebuffer
indexBufQuads int
prog gl.Program
iprog gl.Program
prog Program
iprog Program
fbos fboSet
intersections fboSet
uScale, uOffset gl.Uniform
uPathOffset gl.Uniform
uIntersectUVOffset gl.Uniform
uIntersectUVScale gl.Uniform
indexBuf gl.Buffer
uScale, uOffset Uniform
uPathOffset Uniform
uIntersectUVOffset Uniform
uIntersectUVScale Uniform
indexBuf Buffer
}
type fboSet struct {
@@ -56,27 +56,29 @@ type fboSet struct {
type stencilFBO struct {
size image.Point
fbo gl.Framebuffer
tex gl.Texture
fbo Framebuffer
tex Texture
}
type pathData struct {
ncurves int
data gl.Buffer
data Buffer
}
var (
pathAttribs = []string{"corner", "maxy", "from", "ctrl", "to"}
attribPathCorner gl.Attrib = 0
attribPathMaxY gl.Attrib = 1
attribPathFrom gl.Attrib = 2
attribPathCtrl gl.Attrib = 3
attribPathTo gl.Attrib = 4
pathAttribs = []string{"corner", "maxy", "from", "ctrl", "to"}
intersectAttribs = []string{"pos", "uv"}
)
func newPather(ctx *context) *pather {
const (
attribPathCorner = 0
attribPathMaxY = 1
attribPathFrom = 2
attribPathCtrl = 3
attribPathTo = 4
)
func newPather(ctx Backend) *pather {
return &pather{
ctx: ctx,
stenciler: newStenciler(ctx),
@@ -84,7 +86,7 @@ func newPather(ctx *context) *pather {
}
}
func newCoverer(ctx *context) *coverer {
func newCoverer(ctx Backend) *coverer {
prog, err := createColorPrograms(ctx, coverVSrc, coverFSrc)
if err != nil {
panic(err)
@@ -94,68 +96,58 @@ func newCoverer(ctx *context) *coverer {
prog: prog,
}
for i, prog := range prog {
ctx.UseProgram(prog)
switch materialType(i) {
case materialTexture:
uTex := gl.GetUniformLocation(ctx.Functions, prog, "tex")
ctx.Uniform1i(uTex, 0)
c.vars[i].uUVScale = gl.GetUniformLocation(ctx.Functions, prog, "uvScale")
c.vars[i].uUVOffset = gl.GetUniformLocation(ctx.Functions, prog, "uvOffset")
uTex := prog.UniformFor("tex")
prog.Uniform1i(uTex, 0)
c.vars[i].uUVScale = prog.UniformFor("uvScale")
c.vars[i].uUVOffset = prog.UniformFor("uvOffset")
case materialColor:
c.vars[i].uColor = gl.GetUniformLocation(ctx.Functions, prog, "color")
c.vars[i].uColor = prog.UniformFor("color")
}
uCover := gl.GetUniformLocation(ctx.Functions, prog, "cover")
ctx.Uniform1i(uCover, 1)
c.vars[i].z = gl.GetUniformLocation(ctx.Functions, prog, "z")
c.vars[i].uScale = gl.GetUniformLocation(ctx.Functions, prog, "scale")
c.vars[i].uOffset = gl.GetUniformLocation(ctx.Functions, prog, "offset")
c.vars[i].uCoverUVScale = gl.GetUniformLocation(ctx.Functions, prog, "uvCoverScale")
c.vars[i].uCoverUVOffset = gl.GetUniformLocation(ctx.Functions, prog, "uvCoverOffset")
uCover := prog.UniformFor("cover")
prog.Uniform1i(uCover, 1)
c.vars[i].z = prog.UniformFor("z")
c.vars[i].uScale = prog.UniformFor("scale")
c.vars[i].uOffset = prog.UniformFor("offset")
c.vars[i].uCoverUVScale = prog.UniformFor("uvCoverScale")
c.vars[i].uCoverUVOffset = prog.UniformFor("uvCoverOffset")
}
return c
}
func newStenciler(ctx *context) *stenciler {
defFBO := gl.Framebuffer(ctx.GetBinding(gl.FRAMEBUFFER_BINDING))
prog, err := gl.CreateProgram(ctx.Functions, stencilVSrc, stencilFSrc, pathAttribs)
func newStenciler(ctx Backend) *stenciler {
defFBO := ctx.DefaultFramebuffer()
prog, err := ctx.NewProgram(stencilVSrc, stencilFSrc, pathAttribs)
if err != nil {
panic(err)
}
ctx.UseProgram(prog)
iprog, err := gl.CreateProgram(ctx.Functions, intersectVSrc, intersectFSrc, intersectAttribs)
iprog, err := ctx.NewProgram(intersectVSrc, intersectFSrc, intersectAttribs)
if err != nil {
panic(err)
}
coverLoc := gl.GetUniformLocation(ctx.Functions, iprog, "cover")
ctx.UseProgram(iprog)
ctx.Uniform1i(coverLoc, 0)
coverLoc := iprog.UniformFor("cover")
iprog.Uniform1i(coverLoc, 0)
return &stenciler{
ctx: ctx,
defFBO: defFBO,
prog: prog,
iprog: iprog,
uScale: gl.GetUniformLocation(ctx.Functions, prog, "scale"),
uOffset: gl.GetUniformLocation(ctx.Functions, prog, "offset"),
uPathOffset: gl.GetUniformLocation(ctx.Functions, prog, "pathOffset"),
uIntersectUVScale: gl.GetUniformLocation(ctx.Functions, iprog, "uvScale"),
uIntersectUVOffset: gl.GetUniformLocation(ctx.Functions, iprog, "uvOffset"),
indexBuf: ctx.CreateBuffer(),
uScale: prog.UniformFor("scale"),
uOffset: prog.UniformFor("offset"),
uPathOffset: prog.UniformFor("pathOffset"),
uIntersectUVScale: iprog.UniformFor("uvScale"),
uIntersectUVOffset: iprog.UniformFor("uvOffset"),
indexBuf: ctx.NewBuffer(BufferTypeIndices),
}
}
func (s *fboSet) resize(ctx *context, sizes []image.Point) {
func (s *fboSet) resize(ctx Backend, sizes []image.Point) {
// Add fbos.
for i := len(s.fbos); i < len(sizes); i++ {
tex := ctx.CreateTexture()
ctx.BindTexture(gl.TEXTURE_2D, tex)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
fbo := ctx.CreateFramebuffer()
s.fbos = append(s.fbos, stencilFBO{
fbo: fbo,
tex: tex,
fbo: ctx.NewFramebuffer(),
tex: ctx.NewTexture(FilterNearest, FilterNearest),
})
}
// Resize fbos.
@@ -168,37 +160,33 @@ func (s *fboSet) resize(ctx *context, sizes []image.Point) {
resize = resize || waste > 1.2
if resize {
f.size = sz
ctx.BindTexture(gl.TEXTURE_2D, f.tex)
tt := ctx.caps.floatTriple
ctx.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, sz.X, sz.Y, tt.format, tt.typ, nil)
ctx.BindFramebuffer(gl.FRAMEBUFFER, f.fbo)
ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, f.tex, 0)
f.tex.Resize(TextureFormatFloat, sz.X, sz.Y)
f.fbo.BindTexture(f.tex)
}
}
// Delete extra fbos.
s.delete(ctx, len(sizes))
}
func (s *fboSet) invalidate(ctx *context) {
func (s *fboSet) invalidate(ctx Backend) {
for _, f := range s.fbos {
ctx.BindFramebuffer(gl.FRAMEBUFFER, f.fbo)
ctx.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
f.fbo.Invalidate()
}
}
func (s *fboSet) delete(ctx *context, idx int) {
func (s *fboSet) delete(ctx Backend, idx int) {
for i := idx; i < len(s.fbos); i++ {
f := s.fbos[i]
ctx.DeleteFramebuffer(f.fbo)
ctx.DeleteTexture(f.tex)
f.fbo.Release()
f.tex.Release()
}
s.fbos = s.fbos[:idx]
}
func (s *stenciler) release() {
s.fbos.delete(s.ctx, 0)
s.ctx.DeleteProgram(s.prog)
s.ctx.DeleteBuffer(s.indexBuf)
s.prog.Release()
s.indexBuf.Release()
}
func (p *pather) release() {
@@ -208,22 +196,21 @@ func (p *pather) release() {
func (c *coverer) release() {
for _, p := range c.prog {
c.ctx.DeleteProgram(p)
p.Release()
}
}
func buildPath(ctx *context, p []byte) *pathData {
buf := ctx.CreateBuffer()
ctx.BindBuffer(gl.ARRAY_BUFFER, buf)
ctx.BufferData(gl.ARRAY_BUFFER, p, gl.STATIC_DRAW)
func buildPath(ctx Backend, p []byte) *pathData {
buf := ctx.NewBuffer(BufferTypeData)
buf.Upload(BufferUsageStaticDraw, p)
return &pathData{
ncurves: len(p) / path.VertStride,
data: buf,
}
}
func (p *pathData) release(ctx *context) {
ctx.DeleteBuffer(p.data)
func (p *pathData) release() {
p.data.Release()
}
func (p *pather) begin(sizes []image.Point) {
@@ -239,26 +226,24 @@ func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.
}
func (s *stenciler) beginIntersect(sizes []image.Point) {
s.ctx.ActiveTexture(gl.TEXTURE1)
s.ctx.BindTexture(gl.TEXTURE_2D, gl.Texture{})
s.ctx.ActiveTexture(gl.TEXTURE0)
s.ctx.BlendFunc(gl.DST_COLOR, gl.ZERO)
s.ctx.NilTexture().Bind(1)
s.ctx.BlendFunc(BlendFactorDstColor, BlendFactorZero)
// 8 bit coverage is enough, but OpenGL ES only supports single channel
// floating point formats. Replace with GL_RGB+GL_UNSIGNED_BYTE if
// no floating point support is available.
s.intersections.resize(s.ctx, sizes)
s.ctx.ClearColor(1.0, 0.0, 0.0, 0.0)
s.ctx.UseProgram(s.iprog)
s.iprog.Bind()
}
func (s *stenciler) endIntersect() {
s.ctx.BindFramebuffer(gl.FRAMEBUFFER, s.defFBO)
s.defFBO.Bind()
}
func (s *stenciler) invalidateFBO() {
s.intersections.invalidate(s.ctx)
s.fbos.invalidate(s.ctx)
s.ctx.BindFramebuffer(gl.FRAMEBUFFER, s.defFBO)
s.defFBO.Bind()
}
func (s *stenciler) cover(idx int) stencilFBO {
@@ -266,31 +251,24 @@ func (s *stenciler) cover(idx int) stencilFBO {
}
func (s *stenciler) begin(sizes []image.Point) {
s.ctx.ActiveTexture(gl.TEXTURE1)
s.ctx.BindTexture(gl.TEXTURE_2D, gl.Texture{})
s.ctx.ActiveTexture(gl.TEXTURE0)
s.ctx.BlendFunc(gl.ONE, gl.ONE)
s.ctx.NilTexture().Bind(1)
s.ctx.BlendFunc(BlendFactorOne, BlendFactorOne)
s.fbos.resize(s.ctx, sizes)
s.ctx.ClearColor(0.0, 0.0, 0.0, 0.0)
s.ctx.UseProgram(s.prog)
s.ctx.EnableVertexAttribArray(attribPathCorner)
s.ctx.EnableVertexAttribArray(attribPathMaxY)
s.ctx.EnableVertexAttribArray(attribPathFrom)
s.ctx.EnableVertexAttribArray(attribPathCtrl)
s.ctx.EnableVertexAttribArray(attribPathTo)
s.ctx.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, s.indexBuf)
s.prog.Bind()
s.indexBuf.Bind()
}
func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data *pathData) {
s.ctx.BindBuffer(gl.ARRAY_BUFFER, data.data)
data.data.Bind()
s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy())
// Transform UI coordinates to OpenGL coordinates.
texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y}
orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y}
s.ctx.Uniform2f(s.uScale, scale.X, scale.Y)
s.ctx.Uniform2f(s.uOffset, orig.X, orig.Y)
s.ctx.Uniform2f(s.uPathOffset, offset.X, offset.Y)
s.prog.Uniform2f(s.uScale, scale.X, scale.Y)
s.prog.Uniform2f(s.uOffset, orig.X, orig.Y)
s.prog.Uniform2f(s.uPathOffset, offset.X, offset.Y)
// Draw in batches that fit in uint16 indices.
start := 0
nquads := data.ncurves / 4
@@ -311,27 +289,22 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima
indices[i*6+4] = i*4 + 1
indices[i*6+5] = i*4 + 3
}
s.ctx.BufferData(gl.ELEMENT_ARRAY_BUFFER, gl.BytesView(indices), gl.STATIC_DRAW)
s.indexBuf.Upload(BufferUsageStaticDraw, gunsafe.BytesView(indices))
s.indexBufQuads = batch
}
off := path.VertStride * start * 4
s.ctx.VertexAttribPointer(attribPathCorner, 2, gl.SHORT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).CornerX)))
s.ctx.VertexAttribPointer(attribPathMaxY, 1, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).MaxY)))
s.ctx.VertexAttribPointer(attribPathFrom, 2, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).FromX)))
s.ctx.VertexAttribPointer(attribPathCtrl, 2, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).CtrlX)))
s.ctx.VertexAttribPointer(attribPathTo, 2, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).ToX)))
s.ctx.DrawElements(gl.TRIANGLES, batch*6, gl.UNSIGNED_SHORT, 0)
s.ctx.SetupVertexArray(attribPathCorner, 2, DataTypeShort, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).CornerX)))
s.ctx.SetupVertexArray(attribPathMaxY, 1, DataTypeFloat, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).MaxY)))
s.ctx.SetupVertexArray(attribPathFrom, 2, DataTypeFloat, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).FromX)))
s.ctx.SetupVertexArray(attribPathCtrl, 2, DataTypeFloat, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).CtrlX)))
s.ctx.SetupVertexArray(attribPathTo, 2, DataTypeFloat, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).ToX)))
s.ctx.DrawElements(DrawModeTriangles, 0, batch*6)
start += batch
}
}
func (s *stenciler) end() {
s.ctx.DisableVertexAttribArray(attribPathCorner)
s.ctx.DisableVertexAttribArray(attribPathMaxY)
s.ctx.DisableVertexAttribArray(attribPathFrom)
s.ctx.DisableVertexAttribArray(attribPathCtrl)
s.ctx.DisableVertexAttribArray(attribPathTo)
s.ctx.BindFramebuffer(gl.FRAMEBUFFER, s.defFBO)
s.defFBO.Bind()
}
func (p *pather) cover(z float32, mat materialType, col [4]float32, scale, off, uvScale, uvOff, coverScale, coverOff f32.Point) {
@@ -339,20 +312,21 @@ func (p *pather) cover(z float32, mat materialType, col [4]float32, scale, off,
}
func (c *coverer) cover(z float32, mat materialType, col [4]float32, scale, off, uvScale, uvOff, coverScale, coverOff f32.Point) {
c.ctx.UseProgram(c.prog[mat])
p := c.prog[mat]
p.Bind()
switch mat {
case materialColor:
c.ctx.Uniform4f(c.vars[mat].uColor, col[0], col[1], col[2], col[3])
p.Uniform4f(c.vars[mat].uColor, col[0], col[1], col[2], col[3])
case materialTexture:
c.ctx.Uniform2f(c.vars[mat].uUVScale, uvScale.X, uvScale.Y)
c.ctx.Uniform2f(c.vars[mat].uUVOffset, uvOff.X, uvOff.Y)
p.Uniform2f(c.vars[mat].uUVScale, uvScale.X, uvScale.Y)
p.Uniform2f(c.vars[mat].uUVOffset, uvOff.X, uvOff.Y)
}
c.ctx.Uniform1f(c.vars[mat].z, z)
c.ctx.Uniform2f(c.vars[mat].uScale, scale.X, scale.Y)
c.ctx.Uniform2f(c.vars[mat].uOffset, off.X, off.Y)
c.ctx.Uniform2f(c.vars[mat].uCoverUVScale, coverScale.X, coverScale.Y)
c.ctx.Uniform2f(c.vars[mat].uCoverUVOffset, coverOff.X, coverOff.Y)
c.ctx.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
p.Uniform1f(c.vars[mat].z, z)
p.Uniform2f(c.vars[mat].uScale, scale.X, scale.Y)
p.Uniform2f(c.vars[mat].uOffset, off.X, off.Y)
p.Uniform2f(c.vars[mat].uCoverUVScale, coverScale.X, coverScale.Y)
p.Uniform2f(c.vars[mat].uCoverUVOffset, coverOff.X, coverOff.Y)
c.ctx.DrawArrays(DrawModeTriangleStrip, 0, 4)
}
const stencilVSrc = `