gpu: introduce pipeline abstraction

Modern API such as Metal and Vulkan want clients to compile expensive
state changes into pipeline objects. Change our GPU driver abstraction
to match, thereby paving the way for future drivers.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2021-08-10 16:18:47 +02:00
parent 13b93b27d8
commit afaa31eca8
9 changed files with 548 additions and 400 deletions
+111 -107
View File
@@ -24,10 +24,8 @@ type Backend struct {
// Temporary storage to avoid garbage.
clearColor [4]float32
viewport d3d11.VIEWPORT
blendState blendState
// Current program.
prog *Program
pipeline *Pipeline
caps driver.Caps
@@ -35,15 +33,13 @@ type Backend struct {
fbo *Framebuffer
floatFormat uint32
// cached state objects.
blendStates map[blendState]*d3d11.BlendState
}
type blendState struct {
enable bool
sfactor driver.BlendFactor
dfactor driver.BlendFactor
type Pipeline struct {
vert *d3d11.VertexShader
frag *d3d11.PixelShader
layout *d3d11.InputLayout
blend *d3d11.BlendState
}
type Texture struct {
@@ -57,17 +53,15 @@ type Texture struct {
height int
}
type Program struct {
type VertexShader struct {
backend *Backend
shader *d3d11.VertexShader
src shader.Sources
}
vert struct {
shader *d3d11.VertexShader
uniforms *Buffer
}
frag struct {
shader *d3d11.PixelShader
uniforms *Buffer
}
type FragmentShader struct {
backend *Backend
shader *d3d11.PixelShader
}
type Framebuffer struct {
@@ -86,10 +80,6 @@ type Buffer struct {
immutable bool
}
type InputLayout struct {
layout *d3d11.InputLayout
}
func init() {
driver.NewDirect3D11Device = newDirect3D11Device
}
@@ -122,7 +112,6 @@ func newDirect3D11Device(api driver.Direct3D11) (driver.Device, error) {
MaxTextureSize: 2048, // 9.1 maximum
Features: driver.FeatureSRGB,
},
blendStates: make(map[blendState]*d3d11.BlendState),
}
featLvl := dev.GetFeatureLevel()
if featLvl < d3d11.FEATURE_LEVEL_9_1 {
@@ -191,9 +180,6 @@ func (b *Backend) IsTimeContinuous() bool {
}
func (b *Backend) Release() {
for _, state := range b.blendStates {
d3d11.IUnknownRelease(unsafe.Pointer(state), state.Vtbl.Release)
}
d3d11.IUnknownRelease(unsafe.Pointer(b.ctx), b.ctx.Vtbl.Release)
*b = Backend{}
}
@@ -288,7 +274,7 @@ func (b *Backend) NewFramebuffer(tex driver.Texture) (driver.Framebuffer, error)
return fbo, nil
}
func (b *Backend) NewInputLayout(vertexShader shader.Sources, layout []shader.InputDesc) (driver.InputLayout, error) {
func (b *Backend) newInputLayout(vertexShader shader.Sources, layout []shader.InputDesc) (*d3d11.InputLayout, error) {
if len(vertexShader.Inputs) != len(layout) {
return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vertexShader.Inputs))
}
@@ -333,11 +319,7 @@ func (b *Backend) NewInputLayout(vertexShader shader.Sources, layout []shader.In
AlignedByteOffset: uint32(l.Offset),
}
}
l, err := b.dev.CreateInputLayout(descs, []byte(vertexShader.DXBC))
if err != nil {
return nil, err
}
return &InputLayout{layout: l}, nil
return b.dev.CreateInputLayout(descs, []byte(vertexShader.DXBC))
}
func (b *Backend) NewBuffer(typ driver.BufferBinding, size int) (driver.Buffer, error) {
@@ -385,19 +367,69 @@ func (b *Backend) NewComputeProgram(shader shader.Sources) (driver.Program, erro
panic("not implemented")
}
func (b *Backend) NewProgram(vertexShader, fragmentShader shader.Sources) (driver.Program, error) {
vs, err := b.dev.CreateVertexShader([]byte(vertexShader.DXBC))
func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
vsh := desc.VertexShader.(*VertexShader)
fsh := desc.FragmentShader.(*FragmentShader)
blend, err := b.newBlendState(desc.BlendDesc)
if err != nil {
return nil, err
}
ps, err := b.dev.CreatePixelShader([]byte(fragmentShader.DXBC))
var layout *d3d11.InputLayout
if l := desc.VertexLayout; l != nil {
var err error
layout, err = b.newInputLayout(vsh.src, l)
if err != nil {
d3d11.IUnknownRelease(unsafe.Pointer(blend), blend.Vtbl.AddRef)
return nil, err
}
}
// Retain shaders.
vshRef := vsh.shader
fshRef := fsh.shader
d3d11.IUnknownAddRef(unsafe.Pointer(vshRef), vshRef.Vtbl.AddRef)
d3d11.IUnknownAddRef(unsafe.Pointer(fshRef), fshRef.Vtbl.AddRef)
return &Pipeline{
vert: vshRef,
frag: fshRef,
layout: layout,
blend: blend,
}, nil
}
func (b *Backend) newBlendState(desc driver.BlendDesc) (*d3d11.BlendState, error) {
var d3ddesc d3d11.BLEND_DESC
t0 := &d3ddesc.RenderTarget[0]
t0.RenderTargetWriteMask = d3d11.COLOR_WRITE_ENABLE_ALL
t0.BlendOp = d3d11.BLEND_OP_ADD
t0.BlendOpAlpha = d3d11.BLEND_OP_ADD
if desc.Enable {
t0.BlendEnable = 1
}
scol, salpha := toBlendFactor(desc.SrcFactor)
dcol, dalpha := toBlendFactor(desc.DstFactor)
t0.SrcBlend = scol
t0.SrcBlendAlpha = salpha
t0.DestBlend = dcol
t0.DestBlendAlpha = dalpha
return b.dev.CreateBlendState(&d3ddesc)
}
func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
vs, err := b.dev.CreateVertexShader([]byte(src.DXBC))
if err != nil {
return nil, err
}
p := &Program{backend: b}
p.vert.shader = vs
p.frag.shader = ps
return p, nil
return &VertexShader{b, vs, src}, nil
}
func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
fs, err := b.dev.CreatePixelShader([]byte(src.DXBC))
if err != nil {
return nil, err
}
return &FragmentShader{b, fs}, nil
}
func (b *Backend) Clear(colr, colg, colb, cola float32) {
@@ -428,15 +460,11 @@ func (b *Backend) DrawElements(mode driver.DrawMode, off, count int) {
}
func (b *Backend) prepareDraw(mode driver.DrawMode) {
if p := b.prog; p != nil {
b.ctx.VSSetShader(p.vert.shader)
b.ctx.PSSetShader(p.frag.shader)
if buf := p.vert.uniforms; buf != nil {
b.ctx.VSSetConstantBuffers(buf.buf)
}
if buf := p.frag.uniforms; buf != nil {
b.ctx.PSSetConstantBuffers(buf.buf)
}
if p := b.pipeline; p != nil {
b.ctx.VSSetShader(p.vert)
b.ctx.PSSetShader(p.frag)
b.ctx.IASetInputLayout(p.layout)
b.ctx.OMSetBlendState(p.blend, nil, 0xffffffff)
}
var topology uint32
switch mode {
@@ -448,40 +476,6 @@ func (b *Backend) prepareDraw(mode driver.DrawMode) {
panic("unsupported draw mode")
}
b.ctx.IASetPrimitiveTopology(topology)
blendState, ok := b.blendStates[b.blendState]
if !ok {
var desc d3d11.BLEND_DESC
t0 := &desc.RenderTarget[0]
t0.RenderTargetWriteMask = d3d11.COLOR_WRITE_ENABLE_ALL
t0.BlendOp = d3d11.BLEND_OP_ADD
t0.BlendOpAlpha = d3d11.BLEND_OP_ADD
if b.blendState.enable {
t0.BlendEnable = 1
}
scol, salpha := toBlendFactor(b.blendState.sfactor)
dcol, dalpha := toBlendFactor(b.blendState.dfactor)
t0.SrcBlend = scol
t0.SrcBlendAlpha = salpha
t0.DestBlend = dcol
t0.DestBlendAlpha = dalpha
var err error
blendState, err = b.dev.CreateBlendState(&desc)
if err != nil {
panic(err)
}
b.blendStates[b.blendState] = blendState
}
b.ctx.OMSetBlendState(blendState, nil, 0xffffffff)
}
func (b *Backend) SetBlend(enable bool) {
b.blendState.enable = enable
}
func (b *Backend) BlendFunc(sfactor, dfactor driver.BlendFactor) {
b.blendState.sfactor = sfactor
b.blendState.dfactor = dfactor
}
func (b *Backend) BindImageTexture(unit int, tex driver.Texture, access driver.AccessBits, f driver.TextureFormat) {
@@ -531,27 +525,46 @@ func (b *Backend) BindTexture(unit int, tex driver.Texture) {
b.ctx.PSSetShaderResources(uint32(unit), t.resView)
}
func (b *Backend) BindPipeline(pipe driver.Pipeline) {
b.pipeline = pipe.(*Pipeline)
}
func (b *Backend) BindProgram(prog driver.Program) {
b.prog = prog.(*Program)
}
func (p *Program) Release() {
d3d11.IUnknownRelease(unsafe.Pointer(p.vert.shader), p.vert.shader.Vtbl.Release)
d3d11.IUnknownRelease(unsafe.Pointer(p.frag.shader), p.frag.shader.Vtbl.Release)
p.vert.shader = nil
p.frag.shader = nil
}
func (p *Program) SetStorageBuffer(binding int, buffer driver.Buffer) {
panic("not implemented")
}
func (p *Program) SetVertexUniforms(buf driver.Buffer) {
p.vert.uniforms = buf.(*Buffer)
func (s *VertexShader) Release() {
d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
*s = VertexShader{}
}
func (p *Program) SetFragmentUniforms(buf driver.Buffer) {
p.frag.uniforms = buf.(*Buffer)
func (s *FragmentShader) Release() {
d3d11.IUnknownRelease(unsafe.Pointer(s.shader), s.shader.Vtbl.Release)
*s = FragmentShader{}
}
func (p *Pipeline) Release() {
d3d11.IUnknownRelease(unsafe.Pointer(p.vert), p.vert.Vtbl.Release)
d3d11.IUnknownRelease(unsafe.Pointer(p.frag), p.frag.Vtbl.Release)
d3d11.IUnknownRelease(unsafe.Pointer(p.blend), p.blend.Vtbl.Release)
if l := p.layout; l != nil {
d3d11.IUnknownRelease(unsafe.Pointer(l), l.Vtbl.Release)
}
*p = Pipeline{}
}
func (b *Backend) BindStorageBuffer(binding int, buffer driver.Buffer) {
panic("not implemented")
}
func (b *Backend) BindVertexUniforms(buffer driver.Buffer) {
buf := buffer.(*Buffer)
b.ctx.VSSetConstantBuffers(buf.buf)
}
func (b *Backend) BindFragmentUniforms(buffer driver.Buffer) {
buf := buffer.(*Buffer)
b.ctx.PSSetConstantBuffers(buf.buf)
}
func (b *Backend) BindVertexBuffer(buf driver.Buffer, stride, offset int) {
@@ -650,15 +663,6 @@ func (f *Framebuffer) Release() {
func (f *Framebuffer) ImplementsRenderTarget() {}
func (b *Backend) BindInputLayout(layout driver.InputLayout) {
b.ctx.IASetInputLayout(layout.(*InputLayout).layout)
}
func (l *InputLayout) Release() {
d3d11.IUnknownRelease(unsafe.Pointer(l.layout), l.layout.Vtbl.Release)
l.layout = nil
}
func convBufferBinding(typ driver.BufferBinding) uint32 {
var bindings uint32
if typ&driver.BufferBindingVertices != 0 {
+31 -11
View File
@@ -26,23 +26,25 @@ type Device interface {
NewImmutableBuffer(typ BufferBinding, data []byte) (Buffer, error)
NewBuffer(typ BufferBinding, size int) (Buffer, error)
NewComputeProgram(shader shader.Sources) (Program, error)
NewProgram(vertexShader, fragmentShader shader.Sources) (Program, error)
NewInputLayout(vertexShader shader.Sources, layout []shader.InputDesc) (InputLayout, error)
NewVertexShader(src shader.Sources) (VertexShader, error)
NewFragmentShader(src shader.Sources) (FragmentShader, error)
NewPipeline(desc PipelineDesc) (Pipeline, error)
Clear(r, g, b, a float32)
Viewport(x, y, width, height int)
DrawArrays(mode DrawMode, off, count int)
DrawElements(mode DrawMode, off, count int)
SetBlend(enable bool)
BlendFunc(sfactor, dfactor BlendFactor)
BindInputLayout(i InputLayout)
BindProgram(p Program)
BindPipeline(p Pipeline)
BindFramebuffer(f Framebuffer)
BindTexture(unit int, t Texture)
BindVertexBuffer(b Buffer, stride, offset int)
BindIndexBuffer(b Buffer)
BindImageTexture(unit int, texture Texture, access AccessBits, format TextureFormat)
BindVertexUniforms(buf Buffer)
BindFragmentUniforms(buf Buffer)
BindStorageBuffer(binding int, buf Buffer)
BlitFramebuffer(dst, src Framebuffer, srect, drect image.Rectangle)
MemoryBarrier()
@@ -51,12 +53,23 @@ type Device interface {
Release()
}
// InputLayout is the driver specific representation of the mapping
// between Buffers and shader attributes.
type InputLayout interface {
type Pipeline interface {
Release()
}
type PipelineDesc struct {
VertexShader VertexShader
FragmentShader FragmentShader
VertexLayout []shader.InputDesc
BlendDesc BlendDesc
PixelFormat TextureFormat
}
type BlendDesc struct {
Enable bool
SrcFactor, DstFactor BlendFactor
}
type AccessBits uint8
type BlendFactor uint8
@@ -78,11 +91,16 @@ type Caps struct {
MaxTextureSize int
}
type VertexShader interface {
Release()
}
type FragmentShader interface {
Release()
}
type Program interface {
Release()
SetStorageBuffer(binding int, buf Buffer)
SetVertexUniforms(buf Buffer)
SetFragmentUniforms(buf Buffer)
}
type Buffer interface {
@@ -123,6 +141,8 @@ const (
TextureFormatSRGBA TextureFormat = iota
TextureFormatFloat
TextureFormatRGBA8
// TextureFormatOutput denotes the format used by the output framebuffer.
TextureFormatOutput
)
const (
+150 -110
View File
@@ -32,8 +32,11 @@ type Backend struct {
// textures.
floatTriple textureTriple
// Single channel alpha textures.
alphaTriple textureTriple
srgbaTriple textureTriple
alphaTriple textureTriple
srgbaTriple textureTriple
vertUniforms *buffer
fragUniforms *buffer
storage [storageBindings]*buffer
sRGBFBO *SRGBFBO
@@ -80,9 +83,8 @@ type glState struct {
}
type state struct {
prog *program
layout *inputLayout
buffer bufferBinding
pipeline *pipeline
buffer bufferBinding
}
type bufferBinding struct {
@@ -110,6 +112,13 @@ type framebuffer struct {
foreign bool
}
type pipeline struct {
prog *program
inputs []shader.InputLocation
layout []shader.InputDesc
blend driver.BlendDesc
}
type buffer struct {
backend *Backend
hasBuffer bool
@@ -117,24 +126,26 @@ type buffer struct {
typ driver.BufferBinding
size int
immutable bool
version int
// For emulation of uniform buffers.
data []byte
}
type glshader struct {
backend *Backend
obj gl.Shader
src shader.Sources
}
type program struct {
backend *Backend
obj gl.Program
vertUniforms uniformsTracker
fragUniforms uniformsTracker
storage [storageBindings]*buffer
vertUniforms uniforms
fragUniforms uniforms
}
type uniformsTracker struct {
locs []uniformLocation
size int
buf *buffer
version int
type uniforms struct {
locs []uniformLocation
size int
}
type uniformLocation struct {
@@ -749,11 +760,9 @@ func (b *Backend) MemoryBarrier() {
}
func (b *Backend) DispatchCompute(x, y, z int) {
if p := b.state.prog; p != nil {
for binding, buf := range p.storage {
if buf != nil {
b.glstate.bindBufferBase(b.funcs, gl.SHADER_STORAGE_BUFFER, binding, buf.obj)
}
for binding, buf := range b.storage {
if buf != nil {
b.glstate.bindBufferBase(b.funcs, gl.SHADER_STORAGE_BUFFER, binding, buf.obj)
}
}
b.funcs.DispatchCompute(x, y, z)
@@ -780,11 +789,6 @@ func (b *Backend) BindImageTexture(unit int, tex driver.Texture, access driver.A
b.funcs.BindImageTexture(unit, t.obj, 0, false, 0, acc, format)
}
func (b *Backend) useProgram(p *program) {
b.glstate.useProgram(b.funcs, p.obj)
b.state.prog = p
}
func (b *Backend) BlendFunc(sfactor, dfactor driver.BlendFactor) {
src, dst := toGLBlendFactor(sfactor), toGLBlendFactor(dfactor)
b.glstate.setBlendFuncSeparate(b.funcs, src, dst, src, dst)
@@ -822,12 +826,12 @@ func (b *Backend) DrawArrays(mode driver.DrawMode, off, count int) {
}
func (b *Backend) prepareDraw() {
p := b.state.prog
p := b.state.pipeline
if p == nil {
return
}
b.setupVertexArrays()
p.updateUniforms()
p.prog.updateUniforms()
}
func toGLDrawMode(mode driver.DrawMode) gl.Enum {
@@ -850,21 +854,6 @@ func (b *Backend) Clear(colR, colG, colB, colA float32) {
b.funcs.Clear(gl.COLOR_BUFFER_BIT)
}
func (b *Backend) NewInputLayout(vs shader.Sources, layout []shader.InputDesc) (driver.InputLayout, error) {
if len(vs.Inputs) != len(layout) {
return nil, fmt.Errorf("NewInputLayout: got %d inputs, expected %d", len(layout), len(vs.Inputs))
}
for i, inp := range vs.Inputs {
if exp, got := inp.Size, layout[i].Size; exp != got {
return nil, fmt.Errorf("NewInputLayout: data size mismatch for %q: got %d expected %d", inp.Name, got, exp)
}
}
return &inputLayout{
inputs: vs.Inputs,
layout: layout,
}, nil
}
func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error) {
p, err := gl.CreateComputeProgram(b.funcs, src.GLSL310ES)
if err != nil {
@@ -876,48 +865,94 @@ func (b *Backend) NewComputeProgram(src shader.Sources) (driver.Program, error)
}, nil
}
func (b *Backend) NewProgram(vertShader, fragShader shader.Sources) (driver.Program, error) {
attr := make([]string, len(vertShader.Inputs))
for _, inp := range vertShader.Inputs {
attr[inp.Location] = inp.Name
func (b *Backend) NewVertexShader(src shader.Sources) (driver.VertexShader, error) {
glslSrc := b.glslFor(src)
sh, err := gl.CreateShader(b.funcs, gl.VERTEX_SHADER, glslSrc)
return &glshader{backend: b, obj: sh, src: src}, err
}
func (b *Backend) NewFragmentShader(src shader.Sources) (driver.FragmentShader, error) {
glslSrc := b.glslFor(src)
sh, err := gl.CreateShader(b.funcs, gl.FRAGMENT_SHADER, glslSrc)
return &glshader{backend: b, obj: sh, src: src}, err
}
func (b *Backend) glslFor(src shader.Sources) string {
if b.glver[0] < 3 {
return src.GLSL100ES
}
vsrc, fsrc := vertShader.GLSL100ES, fragShader.GLSL100ES
if b.glver[0] >= 3 {
// OpenGL (ES) 3.0.
switch {
case b.gles:
vsrc, fsrc = vertShader.GLSL300ES, fragShader.GLSL300ES
case b.glver[0] >= 4 || b.glver[1] >= 2:
// OpenGL 3.2 Core only accepts glsl 1.50 or newer.
vsrc, fsrc = vertShader.GLSL150, fragShader.GLSL150
default:
vsrc, fsrc = vertShader.GLSL130, fragShader.GLSL130
}
// OpenGL (ES) 3.0.
switch {
case b.gles:
return src.GLSL300ES
case b.glver[0] >= 4 || b.glver[1] >= 2:
// OpenGL 3.2 Core only accepts glsl 1.50 or newer.
return src.GLSL150
default:
return src.GLSL130
}
p, err := gl.CreateProgram(b.funcs, vsrc, fsrc, attr)
}
func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) {
p, err := b.newProgram(desc)
if err != nil {
return nil, err
}
layout := desc.VertexLayout
vsrc := desc.VertexShader.(*glshader).src
if len(vsrc.Inputs) != len(layout) {
return nil, fmt.Errorf("opengl: got %d inputs, expected %d", len(layout), len(vsrc.Inputs))
}
for i, inp := range vsrc.Inputs {
if exp, got := inp.Size, layout[i].Size; exp != got {
return nil, fmt.Errorf("opengl: data size mismatch for %q: got %d expected %d", inp.Name, got, exp)
}
}
return &pipeline{
prog: p,
inputs: vsrc.Inputs,
layout: layout,
blend: desc.BlendDesc,
}, nil
}
func (b *Backend) newProgram(desc driver.PipelineDesc) (*program, error) {
p := b.funcs.CreateProgram()
if !p.Valid() {
return nil, errors.New("opengl: glCreateProgram failed")
}
vsh, fsh := desc.VertexShader.(*glshader), desc.FragmentShader.(*glshader)
b.funcs.AttachShader(p, vsh.obj)
b.funcs.AttachShader(p, fsh.obj)
for _, inp := range vsh.src.Inputs {
b.funcs.BindAttribLocation(p, gl.Attrib(inp.Location), inp.Name)
}
b.funcs.LinkProgram(p)
if b.funcs.GetProgrami(p, gl.LINK_STATUS) == 0 {
log := b.funcs.GetProgramInfoLog(p)
b.funcs.DeleteProgram(p)
return nil, fmt.Errorf("opengl: program link failed: %s", strings.TrimSpace(log))
}
prog := &program{
backend: b,
obj: p,
}
b.BindProgram(prog)
b.glstate.useProgram(b.funcs, p)
// Bind texture uniforms.
for _, tex := range vertShader.Textures {
for _, tex := range vsh.src.Textures {
u := b.funcs.GetUniformLocation(p, tex.Name)
if u.Valid() {
b.funcs.Uniform1i(u, tex.Binding)
}
}
for _, tex := range fragShader.Textures {
for _, tex := range fsh.src.Textures {
u := b.funcs.GetUniformLocation(p, tex.Name)
if u.Valid() {
b.funcs.Uniform1i(u, tex.Binding)
}
}
if b.ubo {
for _, block := range vertShader.Uniforms.Blocks {
for _, block := range vsh.src.Uniforms.Blocks {
blockIdx := b.funcs.GetUniformBlockIndex(p, block.Name)
if blockIdx != gl.INVALID_INDEX {
b.funcs.UniformBlockBinding(p, blockIdx, uint(block.Binding))
@@ -926,16 +961,16 @@ func (b *Backend) NewProgram(vertShader, fragShader shader.Sources) (driver.Prog
// To match Direct3D 11 with separate vertex and fragment
// shader uniform buffers, offset all fragment blocks to be
// located after the vertex blocks.
off := len(vertShader.Uniforms.Blocks)
for _, block := range fragShader.Uniforms.Blocks {
off := len(vsh.src.Uniforms.Blocks)
for _, block := range fsh.src.Uniforms.Blocks {
blockIdx := b.funcs.GetUniformBlockIndex(p, block.Name)
if blockIdx != gl.INVALID_INDEX {
b.funcs.UniformBlockBinding(p, blockIdx, uint(block.Binding+off))
}
}
} else {
prog.vertUniforms.setup(b.funcs, p, vertShader.Uniforms.Size, vertShader.Uniforms.Locations)
prog.fragUniforms.setup(b.funcs, p, fragShader.Uniforms.Size, fragShader.Uniforms.Locations)
prog.vertUniforms.setup(b.funcs, p, vsh.src.Uniforms.Size, vsh.src.Uniforms.Locations)
prog.fragUniforms.setup(b.funcs, p, fsh.src.Uniforms.Size, fsh.src.Uniforms.Locations)
}
return prog, nil
}
@@ -948,47 +983,59 @@ func lookupUniform(funcs *gl.Functions, p gl.Program, loc shader.UniformLocation
return uniformLocation{uniform: u, offset: loc.Offset, typ: loc.Type, size: loc.Size}
}
func (p *program) SetStorageBuffer(binding int, buf driver.Buffer) {
b := buf.(*buffer)
if b.typ&driver.BufferBindingShaderStorage == 0 {
func (b *Backend) BindStorageBuffer(binding int, buf driver.Buffer) {
bf := buf.(*buffer)
if bf.typ&driver.BufferBindingShaderStorage == 0 {
panic("not a shader storage buffer")
}
p.storage[binding] = b
b.storage[binding] = bf
}
func (p *program) SetVertexUniforms(buf driver.Buffer) {
p.vertUniforms.setBuffer(buf)
func (b *Backend) BindVertexUniforms(buf driver.Buffer) {
bf := buf.(*buffer)
if bf.typ&driver.BufferBindingUniforms == 0 {
panic("not a uniform buffer")
}
b.vertUniforms = bf
}
func (p *program) SetFragmentUniforms(buf driver.Buffer) {
p.fragUniforms.setBuffer(buf)
func (b *Backend) BindFragmentUniforms(buf driver.Buffer) {
bf := buf.(*buffer)
if bf.typ&driver.BufferBindingUniforms == 0 {
panic("not a uniform buffer")
}
b.fragUniforms = bf
}
func (p *program) updateUniforms() {
f := p.backend.funcs
if p.backend.ubo {
if b := p.vertUniforms.buf; b != nil {
if b := p.backend.vertUniforms; b != nil {
p.backend.glstate.bindBufferBase(f, gl.UNIFORM_BUFFER, 0, b.obj)
}
if b := p.fragUniforms.buf; b != nil {
if b := p.backend.fragUniforms; b != nil {
p.backend.glstate.bindBufferBase(f, gl.UNIFORM_BUFFER, 1, b.obj)
}
} else {
p.vertUniforms.update(f)
p.fragUniforms.update(f)
p.vertUniforms.update(f, p.backend.vertUniforms)
p.fragUniforms.update(f, p.backend.fragUniforms)
}
}
func (b *Backend) BindProgram(prog driver.Program) {
p := prog.(*program)
b.useProgram(p)
b.glstate.useProgram(b.funcs, p.obj)
}
func (s *glshader) Release() {
s.backend.funcs.DeleteShader(s.obj)
}
func (p *program) Release() {
p.backend.glstate.deleteProgram(p.backend.funcs, p.obj)
}
func (u *uniformsTracker) setup(funcs *gl.Functions, p gl.Program, uniformSize int, uniforms []shader.UniformLocation) {
func (u *uniforms) setup(funcs *gl.Functions, p gl.Program, uniformSize int, uniforms []shader.UniformLocation) {
u.locs = make([]uniformLocation, len(uniforms))
for i, uniform := range uniforms {
u.locs[i] = lookupUniform(funcs, p, uniform)
@@ -996,26 +1043,11 @@ func (u *uniformsTracker) setup(funcs *gl.Functions, p gl.Program, uniformSize i
u.size = uniformSize
}
func (u *uniformsTracker) setBuffer(buf driver.Buffer) {
b := buf.(*buffer)
if b.typ&driver.BufferBindingUniforms == 0 {
panic("not a uniform buffer")
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))
}
if b.size < u.size {
panic(fmt.Errorf("uniform buffer too small, got %d need %d", b.size, u.size))
}
u.buf = b
// Force update.
u.version = b.version - 1
}
func (p *uniformsTracker) update(funcs *gl.Functions) {
b := p.buf
if b == nil || b.version == p.version {
return
}
p.version = b.version
data := b.data
data := buf.data
for _, u := range p.locs {
data := data[u.offset:]
switch {
@@ -1048,7 +1080,6 @@ func (b *buffer) Upload(data []byte) {
if len(data) > b.size {
panic("buffer size overflow")
}
b.version++
copy(b.data, data)
if b.hasBuffer {
firstBinding := firstBufferType(b.typ)
@@ -1100,15 +1131,17 @@ func (b *Backend) BindVertexBuffer(buf driver.Buffer, stride, offset int) {
}
func (b *Backend) setupVertexArrays() {
layout := b.state.layout
if layout == nil {
p := b.state.pipeline
inputs := p.inputs
if len(inputs) == 0 {
return
}
layout := p.layout
const max = len(b.glstate.vertAttribs)
var enabled [max]bool
buf := b.state.buffer
for i, inp := range layout.inputs {
l := layout.layout[i]
for i, inp := range inputs {
l := layout[i]
var gltyp gl.Enum
switch l.Type {
case shader.DataTypeFloat:
@@ -1154,6 +1187,14 @@ func (f *framebuffer) ReadPixels(src image.Rectangle, pixels []byte) error {
return glErr(f.backend.funcs)
}
func (b *Backend) BindPipeline(pl driver.Pipeline) {
p := pl.(*pipeline)
b.state.pipeline = p
b.glstate.useProgram(b.funcs, p.prog.obj)
b.SetBlend(p.blend.Enable)
b.BlendFunc(p.blend.SrcFactor, p.blend.DstFactor)
}
func (b *Backend) BindFramebuffer(fbo driver.Framebuffer) {
b.glstate.bindFramebuffer(b.funcs, gl.FRAMEBUFFER, fbo.(*framebuffer).obj)
}
@@ -1172,6 +1213,11 @@ func (f *framebuffer) Release() {
func (f *framebuffer) ImplementsRenderTarget() {}
func (p *pipeline) Release() {
p.prog.Release()
*p = pipeline{}
}
func toTexFilter(f driver.TextureFilter) int {
switch f {
case driver.FilterNearest:
@@ -1224,12 +1270,6 @@ func (t *timer) Duration() (time.Duration, bool) {
return time.Duration(nanos), true
}
func (b *Backend) BindInputLayout(l driver.InputLayout) {
b.state.layout = l.(*inputLayout)
}
func (l *inputLayout) Release() {}
// floatTripleFor determines the best texture triple for floating point FBOs.
func floatTripleFor(f *gl.Functions, ver [2]int, exts []string) (textureTriple, error) {
var triples []textureTriple