gpu,gpu/gl: implement shader uniform buffers

Emulate them for the OpenGL ES backend because 2.0 doesn't support uniform
buffers. The future d3d backend only supports uniform (constant) buffers.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2020-02-19 21:06:30 +01:00
parent ef3e94e7a7
commit 646a767665
12 changed files with 504 additions and 252 deletions
+151 -51
View File
@@ -13,6 +13,7 @@ import (
"image"
"image/color"
"math"
"reflect"
"time"
"unsafe"
@@ -209,19 +210,52 @@ type texture struct {
}
type blitter struct {
ctx Backend
viewport image.Point
prog [2]Program
layout InputLayout
vars [2]struct {
z Uniform
uScale, uOffset Uniform
uUVScale, uUVOffset Uniform
uColor Uniform
ctx Backend
viewport image.Point
prog [2]*program
layout InputLayout
colUniforms struct {
vert struct {
blitUniforms
_ [8]byte // Padding to a multiple of 16.
}
frag struct {
colorUniforms
}
}
texUniforms struct {
vert struct {
blitUniforms
_ [8]byte // Padding to a multiple of 16.
}
}
quadVerts Buffer
}
type uniformBuffer struct {
buf Buffer
ptr []byte
}
type program struct {
prog Program
vertUniforms *uniformBuffer
fragUniforms *uniformBuffer
}
type blitUniforms struct {
z float32
_ float32 // Padding.
scale [2]float32
offset [2]float32
uvScale [2]float32
uvOffset [2]float32
}
type colorUniforms struct {
color [4]float32
}
type materialType uint8
const (
@@ -372,10 +406,6 @@ func (r *renderer) release() {
}
func newBlitter(ctx Backend) *blitter {
prog, layout, err := createColorPrograms(ctx, shader_blit_vert, shader_blit_frag)
if err != nil {
panic(err)
}
quadVerts := ctx.NewImmutableBuffer(BufferTypeVertices,
gunsafe.BytesView([]float32{
-1, +1, 0, 0,
@@ -386,24 +416,17 @@ func newBlitter(ctx Backend) *blitter {
)
b := &blitter{
ctx: ctx,
prog: prog,
layout: layout,
quadVerts: quadVerts,
}
for i, prog := range prog {
switch materialType(i) {
case materialTexture:
uTex := prog.UniformFor("tex")
prog.Uniform1i(uTex, 0)
b.vars[i].uUVScale = prog.UniformFor("uniforms.uvScale")
b.vars[i].uUVOffset = prog.UniformFor("uniforms.uvOffset")
case materialColor:
b.vars[i].uColor = prog.UniformFor("color.color")
}
b.vars[i].z = prog.UniformFor("uniforms.z")
b.vars[i].uScale = prog.UniformFor("uniforms.scale")
b.vars[i].uOffset = prog.UniformFor("uniforms.offset")
prog, layout, err := createColorPrograms(ctx, shader_blit_vert, shader_blit_frag,
[2]interface{}{&b.colUniforms.vert, &b.texUniforms.vert}, [2]interface{}{&b.colUniforms.frag, nil})
if err != nil {
panic(err)
}
b.prog = prog
b.layout = layout
texProg := b.prog[materialTexture].prog
texProg.Uniform1i(texProg.UniformFor("tex"), 0)
return b
}
@@ -415,28 +438,47 @@ func (b *blitter) release() {
b.layout.Release()
}
func createColorPrograms(ctx Backend, vsSrc ShaderSources, fsSrc [2]ShaderSources) ([2]Program, InputLayout, error) {
var prog [2]Program
var err error
prog[materialTexture], err = ctx.NewProgram(vsSrc, fsSrc[materialTexture])
func createColorPrograms(b Backend, vsSrc ShaderSources, fsSrc [2]ShaderSources, vertUniforms, fragUniforms [2]interface{}) ([2]*program, InputLayout, error) {
var progs [2]*program
prog, err := b.NewProgram(vsSrc, fsSrc[materialTexture])
if err != nil {
return prog, nil, err
return progs, nil, err
}
prog[materialColor], err = ctx.NewProgram(vsSrc, fsSrc[materialColor])
var vertBuffer *uniformBuffer
if u := vertUniforms[materialTexture]; u != nil {
vertBuffer = newUniformBuffer(b, u)
prog.SetVertexUniforms(vertBuffer.buf)
}
var fragBuffer *uniformBuffer
if u := fragUniforms[materialTexture]; u != nil {
fragBuffer = newUniformBuffer(b, u)
prog.SetFragmentUniforms(fragBuffer.buf)
}
progs[materialTexture] = newProgram(prog, vertBuffer, fragBuffer)
prog, err = b.NewProgram(vsSrc, fsSrc[materialColor])
if err != nil {
prog[materialTexture].Release()
return prog, nil, err
progs[materialTexture].Release()
return progs, nil, err
}
layout, err := ctx.NewInputLayout(vsSrc, []InputDesc{
if u := vertUniforms[materialColor]; u != nil {
vertBuffer = newUniformBuffer(b, u)
prog.SetVertexUniforms(vertBuffer.buf)
}
if u := fragUniforms[materialColor]; u != nil {
fragBuffer = newUniformBuffer(b, u)
prog.SetFragmentUniforms(fragBuffer.buf)
}
progs[materialColor] = newProgram(prog, vertBuffer, fragBuffer)
layout, err := b.NewInputLayout(vsSrc, []InputDesc{
{Type: DataTypeFloat, Size: 2, Offset: 0},
{Type: DataTypeFloat, Size: 2, Offset: 4 * 2},
})
if err != nil {
prog[materialTexture].Release()
prog[materialColor].Release()
return prog, nil, err
progs[materialTexture].Release()
progs[materialColor].Release()
return progs, nil, err
}
return prog, layout, nil
return progs, layout, nil
}
func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
@@ -465,7 +507,7 @@ func (r *renderer) intersect(ops []imageOp) {
fbo := -1
r.pather.stenciler.beginIntersect(r.intersections.sizes)
r.blitter.quadVerts.BindVertex(4*4, 0)
r.pather.stenciler.iprogLayout.Bind()
r.pather.stenciler.iprog.layout.Bind()
for _, img := range ops {
if img.clipType != clipTypeIntersection {
continue
@@ -497,8 +539,9 @@ func (r *renderer) intersectPath(p *pathOp, clip image.Rectangle) {
fbo := r.pather.stenciler.cover(p.place.Idx)
fbo.tex.Bind(0)
coverScale, coverOff := texSpaceTransform(toRectF(uv), fbo.size)
r.pather.stenciler.iprog.Uniform2f(r.pather.stenciler.uIntersectUVScale, coverScale.X, coverScale.Y)
r.pather.stenciler.iprog.Uniform2f(r.pather.stenciler.uIntersectUVOffset, coverOff.X, coverOff.Y)
r.pather.stenciler.iprog.uniforms.vert.uvScale = [2]float32{coverScale.X, coverScale.Y}
r.pather.stenciler.iprog.uniforms.vert.uvOffset = [2]float32{coverOff.X, coverOff.Y}
r.pather.stenciler.iprog.prog.UploadUniforms()
r.ctx.DrawArrays(DrawModeTriangleStrip, 0, 4)
}
@@ -846,20 +889,77 @@ func gamma(r, g, b, a uint32) [4]float32 {
func (b *blitter) blit(z float32, mat materialType, col [4]float32, scale, off, uvScale, uvOff f32.Point) {
p := b.prog[mat]
p.Bind()
p.prog.Bind()
var uniforms *blitUniforms
switch mat {
case materialColor:
p.Uniform4f(b.vars[mat].uColor, col[0], col[1], col[2], col[3])
b.colUniforms.frag.color = col
uniforms = &b.colUniforms.vert.blitUniforms
case materialTexture:
p.Uniform2f(b.vars[mat].uUVScale, uvScale.X, uvScale.Y)
p.Uniform2f(b.vars[mat].uUVOffset, uvOff.X, uvOff.Y)
b.texUniforms.vert.uvScale = [2]float32{uvScale.X, uvScale.Y}
b.texUniforms.vert.uvOffset = [2]float32{uvOff.X, uvOff.Y}
uniforms = &b.texUniforms.vert.blitUniforms
}
p.Uniform1f(b.vars[mat].z, z)
p.Uniform2f(b.vars[mat].uScale, scale.X, scale.Y)
p.Uniform2f(b.vars[mat].uOffset, off.X, off.Y)
uniforms.z = z
uniforms.scale = [2]float32{scale.X, scale.Y}
uniforms.offset = [2]float32{off.X, off.Y}
p.UploadUniforms()
b.ctx.DrawArrays(DrawModeTriangleStrip, 0, 4)
}
// newUniformBuffer creates a new GPU uniform buffer backed by the
// structure uniformBlock points to.
func newUniformBuffer(b Backend, uniformBlock interface{}) *uniformBuffer {
ref := reflect.ValueOf(uniformBlock)
// Determine the size of the uniforms structure, *uniforms.
size := ref.Elem().Type().Size()
// Map the uniforms structure as a byte slice.
ptr := (*[1 << 30]byte)(unsafe.Pointer(ref.Pointer()))[:size:size]
ubuf := b.NewBuffer(BufferTypeUniforms, len(ptr))
return &uniformBuffer{buf: ubuf, ptr: ptr}
}
func (u *uniformBuffer) Upload() {
u.buf.Upload(u.ptr)
}
func (u *uniformBuffer) Release() {
u.buf.Release()
u.buf = nil
}
func newProgram(prog Program, vertUniforms, fragUniforms *uniformBuffer) *program {
if vertUniforms != nil {
prog.SetVertexUniforms(vertUniforms.buf)
}
if fragUniforms != nil {
prog.SetFragmentUniforms(fragUniforms.buf)
}
return &program{prog: prog, vertUniforms: vertUniforms, fragUniforms: fragUniforms}
}
func (p *program) UploadUniforms() {
if p.vertUniforms != nil {
p.vertUniforms.Upload()
}
if p.fragUniforms != nil {
p.fragUniforms.Upload()
}
}
func (p *program) Release() {
p.prog.Release()
p.prog = nil
if p.vertUniforms != nil {
p.vertUniforms.Release()
p.vertUniforms = nil
}
if p.fragUniforms != nil {
p.fragUniforms.Release()
p.fragUniforms = nil
}
}
// texSpaceTransform return the scale and offset that transforms the given subimage
// into quad texture coordinates.
func texSpaceTransform(r f32.Rectangle, bounds image.Point) (f32.Point, f32.Point) {