diff --git a/gpu/compute.go b/gpu/compute.go index 0dd3c9b2..a4cd3408 100644 --- a/gpu/compute.go +++ b/gpu/compute.go @@ -422,9 +422,12 @@ func newCompute(ctx driver.Device) (*compute, error) { pipe, err := ctx.NewPipeline(driver.PipelineDesc{ VertexShader: copyVert, FragmentShader: copyFrag, - VertexLayout: []driver.InputDesc{ - {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, - {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + VertexLayout: driver.VertexLayout{ + Inputs: []driver.InputDesc{ + {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, + {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + }, + Stride: int(unsafe.Sizeof(g.output.layerVertices[0])), }, PixelFormat: driver.TextureFormatOutput, BlendDesc: driver.BlendDesc{ @@ -457,9 +460,12 @@ func newCompute(ctx driver.Device) (*compute, error) { pipe, err = ctx.NewPipeline(driver.PipelineDesc{ VertexShader: materialVert, FragmentShader: materialFrag, - VertexLayout: []driver.InputDesc{ - {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, - {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + VertexLayout: driver.VertexLayout{ + Inputs: []driver.InputDesc{ + {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, + {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + }, + Stride: int(unsafe.Sizeof(g.materials.quads[0])), }, PixelFormat: driver.TextureFormatRGBA8, }) @@ -817,7 +823,7 @@ func (g *compute) blitLayers(viewport image.Point) { vertexData := byteslice.Slice(g.output.layerVertices) g.output.buffer.ensureCapacity(false, g.ctx, driver.BufferBindingVertices, len(vertexData)) g.output.buffer.buffer.Upload(vertexData) - g.ctx.BindVertexBuffer(g.output.buffer.buffer, int(unsafe.Sizeof(g.output.layerVertices[0])), 0) + g.ctx.BindVertexBuffer(g.output.buffer.buffer, 0) g.ctx.BindTexture(0, atlas.image) g.ctx.DrawArrays(driver.DrawModeTriangles, 0, len(g.output.layerVertices)) } @@ -931,7 +937,7 @@ restart: g.ctx.BindFramebuffer(m.fbo, d) g.ctx.Viewport(0, 0, texSize, texSize) g.ctx.BindPipeline(m.pipeline) - g.ctx.BindVertexBuffer(m.buffer.buffer, int(unsafe.Sizeof(m.quads[0])), 0) + g.ctx.BindVertexBuffer(m.buffer.buffer, 0) g.ctx.DrawArrays(driver.DrawModeTriangles, 0, len(m.quads)) return nil } diff --git a/gpu/gpu.go b/gpu/gpu.go index acdb9f69..bf1d0abe 100644 --- a/gpu/gpu.go +++ b/gpu/gpu.go @@ -580,9 +580,12 @@ func createColorPrograms(b driver.Device, vsSrc shader.Sources, fsSrc [3]shader. SrcFactor: driver.BlendFactorOne, DstFactor: driver.BlendFactorOneMinusSrcAlpha, } - layout := []driver.InputDesc{ - {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, - {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + layout := driver.VertexLayout{ + Inputs: []driver.InputDesc{ + {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, + {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + }, + Stride: 4 * 4, } vsh, err := b.NewVertexShader(vsSrc) if err != nil { @@ -702,7 +705,7 @@ func (r *renderer) intersect(ops []imageOp) { } fbo := -1 r.pather.stenciler.beginIntersect(r.intersections.sizes) - r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0) + r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0) for _, img := range ops { if img.clipType != clipTypeIntersection { continue @@ -1093,7 +1096,7 @@ func (d *drawState) materialFor(rect f32.Rectangle, off f32.Point, partTrans f32 } func (r *renderer) drawOps(cache *resourceCache, ops []imageOp) { - r.ctx.BindVertexBuffer(r.blitter.quadVerts, 4*4, 0) + r.ctx.BindVertexBuffer(r.blitter.quadVerts, 0) var coverTex driver.Texture for _, img := range ops { m := img.material diff --git a/gpu/headless/driver_test.go b/gpu/headless/driver_test.go index acad56e1..a7b1f94b 100644 --- a/gpu/headless/driver_test.go +++ b/gpu/headless/driver_test.go @@ -77,12 +77,15 @@ func TestInputShader(t *testing.T) { } defer vsh.Release() defer fsh.Release() - layout := []driver.InputDesc{ - { - Type: shader.DataTypeFloat, - Size: 4, - Offset: 0, + layout := driver.VertexLayout{ + Inputs: []driver.InputDesc{ + { + Type: shader.DataTypeFloat, + Size: 4, + Offset: 0, + }, }, + Stride: 4 * 4, } pipe, err := b.NewPipeline(driver.PipelineDesc{ VertexShader: vsh, @@ -106,7 +109,7 @@ func TestInputShader(t *testing.T) { t.Fatal(err) } defer buf.Release() - b.BindVertexBuffer(buf, 4*4, 0) + b.BindVertexBuffer(buf, 0) b.DrawArrays(driver.DrawModeTriangles, 0, 3) img := screenshot(t, b, fbo, sz) if got := img.RGBAAt(0, 0); got != clearColExpect { diff --git a/gpu/internal/d3d11/d3d11_windows.go b/gpu/internal/d3d11/d3d11_windows.go index 499d3681..16ee99f2 100644 --- a/gpu/internal/d3d11/d3d11_windows.go +++ b/gpu/internal/d3d11/d3d11_windows.go @@ -26,6 +26,10 @@ type Backend struct { viewport d3d11.VIEWPORT pipeline *Pipeline + vert struct { + buffer *Buffer + offset int + } caps driver.Caps @@ -40,6 +44,7 @@ type Pipeline struct { frag *d3d11.PixelShader layout *d3d11.InputLayout blend *d3d11.BlendState + stride int } type Texture struct { @@ -375,9 +380,9 @@ func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) return nil, err } var layout *d3d11.InputLayout - if l := desc.VertexLayout; l != nil { + if l := desc.VertexLayout; l.Stride > 0 { var err error - layout, err = b.newInputLayout(vsh.src, l) + layout, err = b.newInputLayout(vsh.src, l.Inputs) if err != nil { d3d11.IUnknownRelease(unsafe.Pointer(blend), blend.Vtbl.AddRef) return nil, err @@ -394,6 +399,7 @@ func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) vert: vshRef, frag: fshRef, layout: layout, + stride: desc.VertexLayout.Stride, blend: blend, }, nil } @@ -460,6 +466,9 @@ func (b *Backend) prepareDraw(mode driver.DrawMode) { b.ctx.PSSetShader(p.frag) b.ctx.IASetInputLayout(p.layout) b.ctx.OMSetBlendState(p.blend, nil, 0xffffffff) + if b.vert.buffer != nil { + b.ctx.IASetVertexBuffers(b.vert.buffer.buf, uint32(p.stride), uint32(b.vert.offset)) + } } var topology uint32 switch mode { @@ -562,8 +571,9 @@ func (b *Backend) BindFragmentUniforms(buffer driver.Buffer) { b.ctx.PSSetConstantBuffers(buf.buf) } -func (b *Backend) BindVertexBuffer(buf driver.Buffer, stride, offset int) { - b.ctx.IASetVertexBuffers(buf.(*Buffer).buf, uint32(stride), uint32(offset)) +func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) { + b.vert.buffer = buf.(*Buffer) + b.vert.offset = offset } func (b *Backend) BindIndexBuffer(buf driver.Buffer) { diff --git a/gpu/internal/driver/driver.go b/gpu/internal/driver/driver.go index f10cef54..726f1801 100644 --- a/gpu/internal/driver/driver.go +++ b/gpu/internal/driver/driver.go @@ -38,7 +38,7 @@ type Device interface { BindPipeline(p Pipeline) BindFramebuffer(f Framebuffer, a LoadDesc) BindTexture(unit int, t Texture) - BindVertexBuffer(b Buffer, stride, offset int) + BindVertexBuffer(b Buffer, offset int) BindIndexBuffer(b Buffer) BindImageTexture(unit int, texture Texture, access AccessBits, format TextureFormat) BindVertexUniforms(buf Buffer) @@ -69,11 +69,16 @@ type Pipeline interface { type PipelineDesc struct { VertexShader VertexShader FragmentShader FragmentShader - VertexLayout []InputDesc + VertexLayout VertexLayout BlendDesc BlendDesc PixelFormat TextureFormat } +type VertexLayout struct { + Inputs []InputDesc + Stride int +} + // InputDesc describes a vertex attribute as laid out in a Buffer. type InputDesc struct { Type shader.DataType diff --git a/gpu/internal/opengl/opengl.go b/gpu/internal/opengl/opengl.go index 21cb3d4b..34f20e56 100644 --- a/gpu/internal/opengl/opengl.go +++ b/gpu/internal/opengl/opengl.go @@ -90,7 +90,6 @@ type state struct { type bufferBinding struct { obj gl.Buffer offset int - stride int } type timer struct { @@ -115,7 +114,7 @@ type framebuffer struct { type pipeline struct { prog *program inputs []shader.InputLocation - layout []driver.InputDesc + layout driver.VertexLayout blend driver.BlendDesc } @@ -900,11 +899,11 @@ func (b *Backend) NewPipeline(desc driver.PipelineDesc) (driver.Pipeline, error) } 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)) + if len(vsrc.Inputs) != len(layout.Inputs) { + return nil, fmt.Errorf("opengl: got %d inputs, expected %d", len(layout.Inputs), len(vsrc.Inputs)) } for i, inp := range vsrc.Inputs { - if exp, got := inp.Size, layout[i].Size; exp != got { + if exp, got := inp.Size, layout.Inputs[i].Size; exp != got { return nil, fmt.Errorf("opengl: data size mismatch for %q: got %d expected %d", inp.Name, got, exp) } } @@ -1122,12 +1121,12 @@ func (b *buffer) Release() { } } -func (b *Backend) BindVertexBuffer(buf driver.Buffer, stride, offset int) { +func (b *Backend) BindVertexBuffer(buf driver.Buffer, offset int) { gbuf := buf.(*buffer) if gbuf.typ&driver.BufferBindingVertices == 0 { panic("not a vertex buffer") } - b.state.buffer = bufferBinding{obj: gbuf.obj, stride: stride, offset: offset} + b.state.buffer = bufferBinding{obj: gbuf.obj, offset: offset} } func (b *Backend) setupVertexArrays() { @@ -1141,7 +1140,7 @@ func (b *Backend) setupVertexArrays() { var enabled [max]bool buf := b.state.buffer for i, inp := range inputs { - l := layout[i] + l := layout.Inputs[i] var gltyp gl.Enum switch l.Type { case shader.DataTypeFloat: @@ -1152,7 +1151,7 @@ func (b *Backend) setupVertexArrays() { panic("unsupported data type") } enabled[inp.Location] = true - b.glstate.vertexAttribPointer(b.funcs, buf.obj, inp.Location, l.Size, gltyp, false, buf.stride, buf.offset+l.Offset) + b.glstate.vertexAttribPointer(b.funcs, buf.obj, inp.Location, l.Size, gltyp, false, p.layout.Stride, buf.offset+l.Offset) } for i := 0; i < max; i++ { b.glstate.setVertexAttribArray(b.funcs, i, enabled[i]) diff --git a/gpu/path.go b/gpu/path.go index a78692d7..420d9175 100644 --- a/gpu/path.go +++ b/gpu/path.go @@ -187,16 +187,22 @@ func newStenciler(ctx driver.Device) *stenciler { if err != nil { panic(err) } - progLayout := []driver.InputDesc{ - {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).Corner))}, - {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).MaxY))}, - {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).FromX))}, - {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).CtrlX))}, - {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).ToX))}, + progLayout := driver.VertexLayout{ + Inputs: []driver.InputDesc{ + {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).Corner))}, + {Type: shader.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).MaxY))}, + {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).FromX))}, + {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).CtrlX))}, + {Type: shader.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).ToX))}, + }, + Stride: vertStride, } - iprogLayout := []driver.InputDesc{ - {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, - {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + iprogLayout := driver.VertexLayout{ + Inputs: []driver.InputDesc{ + {Type: shader.DataTypeFloat, Size: 2, Offset: 0}, + {Type: shader.DataTypeFloat, Size: 2, Offset: 4 * 2}, + }, + Stride: 4 * 4, } st := &stenciler{ ctx: ctx, @@ -370,7 +376,7 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima batch = max } off := vertStride * start * 4 - s.ctx.BindVertexBuffer(data.data, vertStride, off) + s.ctx.BindVertexBuffer(data.data, off) s.ctx.DrawElements(driver.DrawModeTriangles, 0, batch*6) start += batch }