op/clip, gpu: split complex curves in package gpu instead

This is a first step towards supporting affine drawing transforms.
The rendering algorithm relies on quadratic curves that do not cross
x = 0 more than once, thus curves must be split after any rotation/shear
transforms. Move this logic and the generation of vertices to package gpu.
Also close all curves and draw zero-width edges as preparation for
transform since the will no longer implicitly be vertical with no
effect.

This commit will severely affect performance since vertexes are now
transformed also for cached items, using cpu resources.

Signed-off-by: Viktor <viktor.ogeman@gmail.com>
This commit is contained in:
Viktor
2020-06-20 23:29:48 +02:00
committed by Elias Naur
parent ef70b9252e
commit 5b277757cf
6 changed files with 240 additions and 173 deletions
+42 -9
View File
@@ -6,13 +6,14 @@ package gpu
// Pathfinder (https://github.com/servo/pathfinder).
import (
"encoding/binary"
"image"
"math"
"unsafe"
"gioui.org/f32"
"gioui.org/gpu/backend"
"gioui.org/internal/f32color"
"gioui.org/internal/path"
gunsafe "gioui.org/internal/unsafe"
)
@@ -104,9 +105,33 @@ type pathData struct {
data backend.Buffer
}
// vertex data suitable for passing to vertex programs.
type vertex struct {
// Corner encodes the corner: +0.5 for south, +.25 for east.
Corner float32
MaxY float32
FromX, FromY float32
CtrlX, CtrlY float32
ToX, ToY float32
}
func (v vertex) encode(d []byte, maxy uint32) {
bo := binary.LittleEndian
bo.PutUint32(d[0:], math.Float32bits(v.Corner))
bo.PutUint32(d[4:], maxy)
bo.PutUint32(d[8:], math.Float32bits(v.FromX))
bo.PutUint32(d[12:], math.Float32bits(v.FromY))
bo.PutUint32(d[16:], math.Float32bits(v.CtrlX))
bo.PutUint32(d[20:], math.Float32bits(v.CtrlY))
bo.PutUint32(d[24:], math.Float32bits(v.ToX))
bo.PutUint32(d[28:], math.Float32bits(v.ToY))
}
const (
// Number of path quads per draw batch.
pathBatchSize = 10000
// Size of a vertex as sent to gpu
vertStride = 7*4 + 2*2
)
func newPather(ctx backend.Device) *pather {
@@ -152,11 +177,11 @@ func newStenciler(ctx backend.Device) *stenciler {
panic(err)
}
progLayout, err := ctx.NewInputLayout(shader_stencil_vert, []backend.InputDesc{
{Type: backend.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*path.Vertex)(nil)).Corner))},
{Type: backend.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*path.Vertex)(nil)).MaxY))},
{Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*path.Vertex)(nil)).FromX))},
{Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*path.Vertex)(nil)).CtrlX))},
{Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*path.Vertex)(nil)).ToX))},
{Type: backend.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).Corner))},
{Type: backend.DataTypeFloat, Size: 1, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).MaxY))},
{Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).FromX))},
{Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).CtrlX))},
{Type: backend.DataTypeFloat, Size: 2, Offset: int(unsafe.Offsetof((*(*vertex)(nil)).ToX))},
})
if err != nil {
panic(err)
@@ -269,7 +294,7 @@ func buildPath(ctx backend.Device, p []byte) *pathData {
panic(err)
}
return &pathData{
ncurves: len(p) / path.VertStride,
ncurves: len(p) / vertStride,
data: buf,
}
}
@@ -329,8 +354,8 @@ func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv ima
if max := pathBatchSize; batch > max {
batch = max
}
off := path.VertStride * start * 4
s.ctx.BindVertexBuffer(data.data, path.VertStride, off)
off := vertStride * start * 4
s.ctx.BindVertexBuffer(data.data, vertStride, off)
s.ctx.DrawElements(backend.DrawModeTriangles, 0, batch*6)
start += batch
}
@@ -358,3 +383,11 @@ func (c *coverer) cover(z float32, mat materialType, col f32color.RGBA, scale, o
p.UploadUniforms()
c.ctx.DrawArrays(backend.DrawModeTriangleStrip, 0, 4)
}
func init() {
// Check that struct vertex has the expected size and
// that it contains no padding.
if unsafe.Sizeof(*(*vertex)(nil)) != vertStride {
panic("unexpected struct size")
}
}