mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
gpu: reintroduce reuse of offset-only stenciling
Reintroduce support for offset in stencil vertex so we can reuse cached values if the only difference in transform is offset. Split current transform into a pure-offset part and the rest and use only the complex part as cache key. Signed-off-by: Viktor <viktor.ogeman@gmail.com>
This commit is contained in:
+29
-14
@@ -83,6 +83,7 @@ type drawState struct {
|
||||
}
|
||||
|
||||
type pathOp struct {
|
||||
off f32.Point
|
||||
// clip is the union of all
|
||||
// later clip rectangles.
|
||||
clip image.Rectangle
|
||||
@@ -97,6 +98,7 @@ type pathOp struct {
|
||||
type imageOp struct {
|
||||
z float32
|
||||
path *pathOp
|
||||
off f32.Point
|
||||
clip image.Rectangle
|
||||
material material
|
||||
clipType clipType
|
||||
@@ -510,7 +512,7 @@ func (r *renderer) stencilClips(pathCache *opCache, ops []*pathOp) {
|
||||
r.ctx.Clear(0.0, 0.0, 0.0, 0.0)
|
||||
}
|
||||
v, _ := pathCache.get(p.pathKey)
|
||||
r.pather.stencilPath(p.clip, p.place.Pos, v.data)
|
||||
r.pather.stencilPath(p.clip, p.off, p.place.Pos, v.data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,11 +687,12 @@ func (d *drawOps) newPathOp() *pathOp {
|
||||
return &d.pathOpCache[len(d.pathOpCache)-1]
|
||||
}
|
||||
|
||||
func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey ops.Key, bounds f32.Rectangle) {
|
||||
func (d *drawOps) addClipPath(state *drawState, aux []byte, auxKey ops.Key, bounds f32.Rectangle, off f32.Point) {
|
||||
npath := d.newPathOp()
|
||||
*npath = pathOp{
|
||||
parent: state.cpath,
|
||||
bounds: bounds,
|
||||
off: off,
|
||||
}
|
||||
state.cpath = npath
|
||||
if len(aux) > 0 {
|
||||
@@ -708,6 +711,15 @@ func (d *drawOps) noCacheKey() ops.Key {
|
||||
return d.reader.NewKey(d.uniqueKeyCounter)
|
||||
}
|
||||
|
||||
// split a transform into two parts, one which is pur offset and the
|
||||
// other representing the scaling, shearing and rotation part
|
||||
func splitTransform(t f32.Affine2D) (srs f32.Affine2D, offset f32.Point) {
|
||||
sx, hx, ox, sy, hy, oy := t.Elems()
|
||||
offset = f32.Point{X: ox, Y: oy}
|
||||
srs = f32.NewAffine2D(sx, hx, 0, sy, hy, 0)
|
||||
return
|
||||
}
|
||||
|
||||
func (d *drawOps) collectOps(r *ops.Reader, state drawState) int {
|
||||
var aux []byte
|
||||
var auxKey ops.Key
|
||||
@@ -726,27 +738,28 @@ loop:
|
||||
var op clipOp
|
||||
op.decode(encOp.Data)
|
||||
bounds := op.bounds
|
||||
trans, off := splitTransform(state.t)
|
||||
if len(aux) > 0 {
|
||||
// There is a clipping path, build the gpu data and update the
|
||||
// cache key such that it will be equal only if the transform is the
|
||||
// same also. Use cached data if we have it.
|
||||
auxKey = auxKey.SetTransform(state.t)
|
||||
auxKey = auxKey.SetTransform(trans)
|
||||
if v, ok := d.pathCache.get(auxKey); ok {
|
||||
// Since the GPU data exists in the cache aux will not be used.
|
||||
op.bounds = v.bounds
|
||||
} else {
|
||||
aux, op.bounds = d.buildVerts(aux, state.t)
|
||||
aux, op.bounds = d.buildVerts(aux, trans)
|
||||
// this will be added to the cache when building the paths later
|
||||
}
|
||||
} else {
|
||||
aux, op.bounds, _ = d.boundsForTransformedRect(bounds, state.t)
|
||||
aux, op.bounds, _ = d.boundsForTransformedRect(bounds, trans)
|
||||
auxKey = d.noCacheKey()
|
||||
}
|
||||
state.clip = state.clip.Intersect(op.bounds)
|
||||
state.clip = state.clip.Intersect(op.bounds.Add(off))
|
||||
if state.clip.Empty() {
|
||||
continue
|
||||
}
|
||||
|
||||
d.addClipPath(&state, aux, auxKey, op.bounds)
|
||||
d.addClipPath(&state, aux, auxKey, op.bounds, off)
|
||||
aux = nil
|
||||
auxKey = ops.Key{}
|
||||
case opconst.TypeColor:
|
||||
@@ -760,8 +773,9 @@ loop:
|
||||
// Transform (if needed) the painting rectangle and if so generate a clip path,
|
||||
// for those cases also compute a partialTrans that maps texture coordinates between
|
||||
// the new bounding rectangle and the transformed original paint rectangle.
|
||||
clipData, bnd, partialTrans := d.boundsForTransformedRect(op.Rect, state.t)
|
||||
clip := state.clip.Intersect(bnd).Canon()
|
||||
trans, off := splitTransform(state.t)
|
||||
clipData, bnd, partialTrans := d.boundsForTransformedRect(op.Rect, trans)
|
||||
clip := state.clip.Intersect(bnd.Add(off)).Canon()
|
||||
if clip.Empty() {
|
||||
continue
|
||||
}
|
||||
@@ -770,11 +784,11 @@ loop:
|
||||
if clipData != nil {
|
||||
// The paint operation is sheared or rotated, add a clip path representing
|
||||
// this transformed rectangle.
|
||||
d.addClipPath(&state, clipData, d.noCacheKey(), bnd)
|
||||
d.addClipPath(&state, clipData, d.noCacheKey(), bnd, off)
|
||||
}
|
||||
|
||||
bounds := boundRectF(clip)
|
||||
mat := state.materialFor(d.cache, bnd, partialTrans, bounds)
|
||||
mat := state.materialFor(d.cache, bnd, off, partialTrans, bounds)
|
||||
|
||||
if bounds.Min == (image.Point{}) && bounds.Max == d.viewport && state.rect && mat.opaque && mat.material == materialColor {
|
||||
// The image is a uniform opaque color and takes up the whole screen.
|
||||
@@ -797,6 +811,7 @@ loop:
|
||||
img := imageOp{
|
||||
z: zf,
|
||||
path: state.cpath,
|
||||
off: off,
|
||||
clip: bounds,
|
||||
material: mat,
|
||||
}
|
||||
@@ -831,7 +846,7 @@ func expandPathOp(p *pathOp, clip image.Rectangle) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, trans f32.Affine2D, clip image.Rectangle) material {
|
||||
func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, off f32.Point, trans f32.Affine2D, clip image.Rectangle) material {
|
||||
var m material
|
||||
switch d.matType {
|
||||
case materialColor:
|
||||
@@ -840,7 +855,7 @@ func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, trans
|
||||
m.opaque = m.color.A == 1.0
|
||||
case materialTexture:
|
||||
m.material = materialTexture
|
||||
dr := boundRectF(rect)
|
||||
dr := boundRectF(rect.Add(off))
|
||||
sz := d.image.src.Bounds().Size()
|
||||
sr := layout.FRect(d.image.rect)
|
||||
if dx := float32(dr.Dx()); dx != 0 {
|
||||
|
||||
+7
-4
@@ -78,7 +78,9 @@ type stenciler struct {
|
||||
|
||||
type stencilUniforms struct {
|
||||
vert struct {
|
||||
transform [4]float32
|
||||
transform [4]float32
|
||||
pathOffset [2]float32
|
||||
_ [8]byte // Padding to multiple of 16.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,8 +308,8 @@ func (p *pather) begin(sizes []image.Point) {
|
||||
p.stenciler.begin(sizes)
|
||||
}
|
||||
|
||||
func (p *pather) stencilPath(bounds image.Rectangle, uv image.Point, data *pathData) {
|
||||
p.stenciler.stencilPath(bounds, uv, data)
|
||||
func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data *pathData) {
|
||||
p.stenciler.stencilPath(bounds, offset, uv, data)
|
||||
}
|
||||
|
||||
func (s *stenciler) beginIntersect(sizes []image.Point) {
|
||||
@@ -336,13 +338,14 @@ func (s *stenciler) begin(sizes []image.Point) {
|
||||
s.ctx.BindIndexBuffer(s.indexBuf)
|
||||
}
|
||||
|
||||
func (s *stenciler) stencilPath(bounds image.Rectangle, uv image.Point, data *pathData) {
|
||||
func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data *pathData) {
|
||||
s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy())
|
||||
// Transform UI coordinates to OpenGL coordinates.
|
||||
texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
|
||||
scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y}
|
||||
orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y}
|
||||
s.prog.uniforms.vert.transform = [4]float32{scale.X, scale.Y, orig.X, orig.Y}
|
||||
s.prog.uniforms.vert.pathOffset = [2]float32{offset.X, offset.Y}
|
||||
s.prog.prog.UploadUniforms()
|
||||
// Draw in batches that fit in uint16 indices.
|
||||
start := 0
|
||||
|
||||
+14
-13
File diff suppressed because one or more lines are too long
@@ -6,6 +6,7 @@ precision highp float;
|
||||
|
||||
layout(binding = 0) uniform Block {
|
||||
vec4 transform;
|
||||
vec2 pathOffset;
|
||||
};
|
||||
|
||||
layout(location=0) in float corner;
|
||||
@@ -22,10 +23,10 @@ void main() {
|
||||
// Add a one pixel overlap so curve quads cover their
|
||||
// entire curves. Could use conservative rasterization
|
||||
// if available.
|
||||
vec2 from = from;
|
||||
vec2 ctrl = ctrl;
|
||||
vec2 to = to;
|
||||
float maxy = maxy;
|
||||
vec2 from = from + pathOffset;
|
||||
vec2 ctrl = ctrl + pathOffset;
|
||||
vec2 to = to + pathOffset;
|
||||
float maxy = maxy + pathOffset.y;
|
||||
vec2 pos;
|
||||
float c = corner;
|
||||
if (c >= 0.375) {
|
||||
|
||||
Reference in New Issue
Block a user