internal/stroke, gpu: reuse slice when splitting cubics

When building GPU vertices from paths, we call stroke.SplitCubic once
per OpCubic. Before this change, each call to stroke.SplitCubic would
allocate a slice, which we would only use to iterate over.

This allocation can be easily avoided by reusing the slice. We can
conveniently store it in gpu.quadSplitter.

In a real application that renders hundreds of paths with tens of
rounded rectangles per path, this saved roughly 4500 allocations (or 1
MB worth) per frame.

Signed-off-by: Dominik Honnef <dominik@honnef.co>
This commit is contained in:
Dominik Honnef
2023-06-15 01:31:40 +02:00
committed by Elias Naur
parent 90e57c2b18
commit b183774063
3 changed files with 11 additions and 4 deletions
+5 -3
View File
@@ -621,6 +621,7 @@ func StrokePathCommands(style StrokeStyle, scene []byte) StrokeQuads {
// decodeToStrokeQuads decodes scene commands to quads ready to stroke.
func decodeToStrokeQuads(pathData []byte) StrokeQuads {
quads := make(StrokeQuads, 0, 2*len(pathData)/(scene.CommandSize+4))
scratch := make([]QuadSegment, 0, 10)
for len(pathData) >= scene.CommandSize+4 {
contour := binary.LittleEndian.Uint32(pathData)
cmd := ops.DecodeCommand(pathData[4:])
@@ -645,7 +646,9 @@ func decodeToStrokeQuads(pathData []byte) StrokeQuads {
}
quads = append(quads, quad)
case scene.OpCubic:
for _, q := range SplitCubic(scene.DecodeCubic(cmd)) {
from, ctrl0, ctrl1, to := scene.DecodeCubic(cmd)
scratch = SplitCubic(from, ctrl0, ctrl1, to, scratch[:0])
for _, q := range scratch {
quad := StrokeQuad{
Contour: contour,
Quad: q,
@@ -660,8 +663,7 @@ func decodeToStrokeQuads(pathData []byte) StrokeQuads {
return quads
}
func SplitCubic(from, ctrl0, ctrl1, to f32.Point) []QuadSegment {
quads := make([]QuadSegment, 0, 10)
func SplitCubic(from, ctrl0, ctrl1, to f32.Point, quads []QuadSegment) []QuadSegment {
// Set the maximum distance proportionally to the longest side
// of the bounding rectangle.
hull := f32.Rectangle{