mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 18:05:35 +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 {
|
type pathOp struct {
|
||||||
|
off f32.Point
|
||||||
// clip is the union of all
|
// clip is the union of all
|
||||||
// later clip rectangles.
|
// later clip rectangles.
|
||||||
clip image.Rectangle
|
clip image.Rectangle
|
||||||
@@ -97,6 +98,7 @@ type pathOp struct {
|
|||||||
type imageOp struct {
|
type imageOp struct {
|
||||||
z float32
|
z float32
|
||||||
path *pathOp
|
path *pathOp
|
||||||
|
off f32.Point
|
||||||
clip image.Rectangle
|
clip image.Rectangle
|
||||||
material material
|
material material
|
||||||
clipType clipType
|
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)
|
r.ctx.Clear(0.0, 0.0, 0.0, 0.0)
|
||||||
}
|
}
|
||||||
v, _ := pathCache.get(p.pathKey)
|
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]
|
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 := d.newPathOp()
|
||||||
*npath = pathOp{
|
*npath = pathOp{
|
||||||
parent: state.cpath,
|
parent: state.cpath,
|
||||||
bounds: bounds,
|
bounds: bounds,
|
||||||
|
off: off,
|
||||||
}
|
}
|
||||||
state.cpath = npath
|
state.cpath = npath
|
||||||
if len(aux) > 0 {
|
if len(aux) > 0 {
|
||||||
@@ -708,6 +711,15 @@ func (d *drawOps) noCacheKey() ops.Key {
|
|||||||
return d.reader.NewKey(d.uniqueKeyCounter)
|
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 {
|
func (d *drawOps) collectOps(r *ops.Reader, state drawState) int {
|
||||||
var aux []byte
|
var aux []byte
|
||||||
var auxKey ops.Key
|
var auxKey ops.Key
|
||||||
@@ -726,27 +738,28 @@ loop:
|
|||||||
var op clipOp
|
var op clipOp
|
||||||
op.decode(encOp.Data)
|
op.decode(encOp.Data)
|
||||||
bounds := op.bounds
|
bounds := op.bounds
|
||||||
|
trans, off := splitTransform(state.t)
|
||||||
if len(aux) > 0 {
|
if len(aux) > 0 {
|
||||||
// There is a clipping path, build the gpu data and update the
|
// 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
|
// cache key such that it will be equal only if the transform is the
|
||||||
// same also. Use cached data if we have it.
|
// 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 {
|
if v, ok := d.pathCache.get(auxKey); ok {
|
||||||
// Since the GPU data exists in the cache aux will not be used.
|
// Since the GPU data exists in the cache aux will not be used.
|
||||||
op.bounds = v.bounds
|
op.bounds = v.bounds
|
||||||
} else {
|
} 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 {
|
} else {
|
||||||
aux, op.bounds, _ = d.boundsForTransformedRect(bounds, state.t)
|
aux, op.bounds, _ = d.boundsForTransformedRect(bounds, trans)
|
||||||
auxKey = d.noCacheKey()
|
auxKey = d.noCacheKey()
|
||||||
}
|
}
|
||||||
state.clip = state.clip.Intersect(op.bounds)
|
state.clip = state.clip.Intersect(op.bounds.Add(off))
|
||||||
if state.clip.Empty() {
|
if state.clip.Empty() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
d.addClipPath(&state, aux, auxKey, op.bounds, off)
|
||||||
d.addClipPath(&state, aux, auxKey, op.bounds)
|
|
||||||
aux = nil
|
aux = nil
|
||||||
auxKey = ops.Key{}
|
auxKey = ops.Key{}
|
||||||
case opconst.TypeColor:
|
case opconst.TypeColor:
|
||||||
@@ -760,8 +773,9 @@ loop:
|
|||||||
// Transform (if needed) the painting rectangle and if so generate a clip path,
|
// 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
|
// for those cases also compute a partialTrans that maps texture coordinates between
|
||||||
// the new bounding rectangle and the transformed original paint rectangle.
|
// the new bounding rectangle and the transformed original paint rectangle.
|
||||||
clipData, bnd, partialTrans := d.boundsForTransformedRect(op.Rect, state.t)
|
trans, off := splitTransform(state.t)
|
||||||
clip := state.clip.Intersect(bnd).Canon()
|
clipData, bnd, partialTrans := d.boundsForTransformedRect(op.Rect, trans)
|
||||||
|
clip := state.clip.Intersect(bnd.Add(off)).Canon()
|
||||||
if clip.Empty() {
|
if clip.Empty() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -770,11 +784,11 @@ loop:
|
|||||||
if clipData != nil {
|
if clipData != nil {
|
||||||
// The paint operation is sheared or rotated, add a clip path representing
|
// The paint operation is sheared or rotated, add a clip path representing
|
||||||
// this transformed rectangle.
|
// this transformed rectangle.
|
||||||
d.addClipPath(&state, clipData, d.noCacheKey(), bnd)
|
d.addClipPath(&state, clipData, d.noCacheKey(), bnd, off)
|
||||||
}
|
}
|
||||||
|
|
||||||
bounds := boundRectF(clip)
|
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 {
|
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.
|
// The image is a uniform opaque color and takes up the whole screen.
|
||||||
@@ -797,6 +811,7 @@ loop:
|
|||||||
img := imageOp{
|
img := imageOp{
|
||||||
z: zf,
|
z: zf,
|
||||||
path: state.cpath,
|
path: state.cpath,
|
||||||
|
off: off,
|
||||||
clip: bounds,
|
clip: bounds,
|
||||||
material: mat,
|
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
|
var m material
|
||||||
switch d.matType {
|
switch d.matType {
|
||||||
case materialColor:
|
case materialColor:
|
||||||
@@ -840,7 +855,7 @@ func (d *drawState) materialFor(cache *resourceCache, rect f32.Rectangle, trans
|
|||||||
m.opaque = m.color.A == 1.0
|
m.opaque = m.color.A == 1.0
|
||||||
case materialTexture:
|
case materialTexture:
|
||||||
m.material = materialTexture
|
m.material = materialTexture
|
||||||
dr := boundRectF(rect)
|
dr := boundRectF(rect.Add(off))
|
||||||
sz := d.image.src.Bounds().Size()
|
sz := d.image.src.Bounds().Size()
|
||||||
sr := layout.FRect(d.image.rect)
|
sr := layout.FRect(d.image.rect)
|
||||||
if dx := float32(dr.Dx()); dx != 0 {
|
if dx := float32(dr.Dx()); dx != 0 {
|
||||||
|
|||||||
+7
-4
@@ -78,7 +78,9 @@ type stenciler struct {
|
|||||||
|
|
||||||
type stencilUniforms struct {
|
type stencilUniforms struct {
|
||||||
vert 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)
|
p.stenciler.begin(sizes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pather) stencilPath(bounds image.Rectangle, uv image.Point, data *pathData) {
|
func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data *pathData) {
|
||||||
p.stenciler.stencilPath(bounds, uv, data)
|
p.stenciler.stencilPath(bounds, offset, uv, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *stenciler) beginIntersect(sizes []image.Point) {
|
func (s *stenciler) beginIntersect(sizes []image.Point) {
|
||||||
@@ -336,13 +338,14 @@ func (s *stenciler) begin(sizes []image.Point) {
|
|||||||
s.ctx.BindIndexBuffer(s.indexBuf)
|
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())
|
s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy())
|
||||||
// Transform UI coordinates to OpenGL coordinates.
|
// Transform UI coordinates to OpenGL coordinates.
|
||||||
texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
|
texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
|
||||||
scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y}
|
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}
|
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.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()
|
s.prog.prog.UploadUniforms()
|
||||||
// Draw in batches that fit in uint16 indices.
|
// Draw in batches that fit in uint16 indices.
|
||||||
start := 0
|
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 {
|
layout(binding = 0) uniform Block {
|
||||||
vec4 transform;
|
vec4 transform;
|
||||||
|
vec2 pathOffset;
|
||||||
};
|
};
|
||||||
|
|
||||||
layout(location=0) in float corner;
|
layout(location=0) in float corner;
|
||||||
@@ -22,10 +23,10 @@ void main() {
|
|||||||
// Add a one pixel overlap so curve quads cover their
|
// Add a one pixel overlap so curve quads cover their
|
||||||
// entire curves. Could use conservative rasterization
|
// entire curves. Could use conservative rasterization
|
||||||
// if available.
|
// if available.
|
||||||
vec2 from = from;
|
vec2 from = from + pathOffset;
|
||||||
vec2 ctrl = ctrl;
|
vec2 ctrl = ctrl + pathOffset;
|
||||||
vec2 to = to;
|
vec2 to = to + pathOffset;
|
||||||
float maxy = maxy;
|
float maxy = maxy + pathOffset.y;
|
||||||
vec2 pos;
|
vec2 pos;
|
||||||
float c = corner;
|
float c = corner;
|
||||||
if (c >= 0.375) {
|
if (c >= 0.375) {
|
||||||
|
|||||||
Reference in New Issue
Block a user