Skip to content

Improve redraw performance by making Clip be a Region instead of Rectangle #3413

@tig

Description

@tig

TUI apps need to be performant under highly-constrained conditions, such as over SSH. Currently the Terminal.Gui system for drawing tries to optimize/reduce redraws via ConsoleDriver.Clip which is a rectangle. This works ok but a system based on an irregular Region could be far more efficient.

Ages ago I experimented with re-implementing Clip using a list of rectangles in this PR:

Note I somehow overwrote the goodness in that PR, but much of the work is in older commits, but the essence was:

		/// <summary>
		/// Gets or sets the clip rectangle that <see cref="AddRune(Rune)"/> and <see cref="AddStr(ustring)"/> are 
		/// subject to. Setting this property is equivalent to calling <see cref="ClearClipRegion()"/>
		/// and <see cref="AddToClipRegion(Rect)"/>.
		/// </summary>
		/// <value>The rectangle describing the bounds of <see cref="ClipRegion"/>.</value>
		public Rect Clip {
			get {
				if (ClipRegion.Count == 0) {
					return new Rect (0, 0, Cols, Rows);
				}

				int minX = ClipRegion.Min (rect => rect.X);
				int minY = ClipRegion.Min (rect => rect.Y);
				int maxX = ClipRegion.Max (rect => rect.X + rect.Width);
				int maxY = ClipRegion.Max (rect => rect.Y + rect.Height);

				return new Rect (minX, minY, maxX - minX, maxY - minY);
			}
			set {
				ClearClipRegion ();
				AddToClipRegion (value);
			}
		}

		List<Rect> _clipRegion = new List<Rect> ();

I abandoned that PR because, at the time, ConsoleDriver and all the implementations were too fragile with way too much duplicated code. I also realized my "list of rectangles" idea was too naive and I was re-inventing the wheel: System.Drawing.Region already exists.

At some point, tackling this PR will be worthy.

If we can't get to it before releasing v2, at the least we should ensure the current View.SetClip API can be forward-compatible so fixing this Issue is not a breaking change to v2.

Other Discussions

I don't think an array of Rectangles is the right approach. I think we want to build the next-gen clipping support on a Region class that is either directly this:

https://learn.microsoft.com/en-us/dotnet/api/system.drawing.region?view=dotnet-plat-ext-8.0

Or, is a clone of it.

So, unless you built your experiment on Region, no.

By chance I noticed the Region class but I didn't use it to avoid having to add another library. I created a RectangleExtensions class to manipulate its Rectangle methods with the array. But since it didn't solve the problem with flickering and I didn't use the Region class, I'm going to give up on continuing with my PR.

Originally posted by @BDisp in #3408 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    ✅ Done

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions