Skip to content

Conversation

@tomcur
Copy link
Member

@tomcur tomcur commented Sep 3, 2025

We're especially interested in marking Ellipse::radii inline, motivated by linebender/vello#1180.

We're especially interested in marking `Ellipse::radii` inline,
motivated by linebender/vello#1180.
@tomcur tomcur added this pull request to the merge queue Sep 4, 2025
Merged via the queue into linebender:main with commit 7e39c40 Sep 4, 2025
15 checks passed
@tomcur tomcur deleted the ellipse-inline branch September 4, 2025 08:41
github-merge-queue bot pushed a commit that referenced this pull request Sep 4, 2025
This adds methods to `Ellipse` to get the major and minor radii.

This documents the (private) `Affine::svd` method to guarantee the part
of its behavior we're using here for efficiency.

The `Ellipse::radii()` method effectively already allows for this, by
taking the `x` component for the major radius and the `y` component for
the minor radius; however, we do not currently guarantee that behavior,
and may not want to, to keep the option of changing internal
representations. Plus, within crate boundaries and the specified
inlining, the compiler should be able to eliminate the dead singular
value decomposition code.

Quickly looking over the assembly, there are just the two `sqrtsd`
instructions as expected.

Like #496, this is motivated by
linebender/vello#1180, to allow eliding more
computations.

<details>
<summary>x86 assembly</summary>

```assembly
		// kurbo/src/ellipse.rs:132
		pub fn major_radius(&self) -> f64 {
	.cfi_startproc
	sub rsp, 104
	.cfi_def_cfa_offset 112
		// kurbo/src/ellipse.rs:133
		self.inner.svd().0.x
	movupd xmm0, xmmword ptr [rdi]
	movupd xmm4, xmmword ptr [rdi + 16]
		// kurbo/src/affine.rs:401
		let a2 = a * a;
	movapd xmm3, xmm0
	mulpd xmm3, xmm0
		// kurbo/src/affine.rs:405
		let ab = a * b;
	movapd xmm2, xmm0
	unpcklpd xmm2, xmm4
	unpckhpd xmm0, xmm4
		// kurbo/src/affine.rs:403
		let c2 = c * c;
	mulpd xmm4, xmm4
		// kurbo/src/affine.rs:405
		let ab = a * b;
	mulpd xmm0, xmm2
		// kurbo/src/affine.rs:407
		let angle = 0.5 * (2.0 * (ab + cd)).atan2(a2 - b2 + c2 - d2);
	movapd xmm1, xmm0
	unpckhpd xmm1, xmm0
	addsd xmm1, xmm0
	movapd xmmword ptr [rsp + 80], xmm1
	movapd xmm0, xmm1
	addsd xmm0, xmm1
	movapd xmmword ptr [rsp + 48], xmm3
		// kurbo/src/affine.rs:408
		let s1 = a2 + b2 + c2 + d2;
	movapd xmm2, xmm3
	unpckhpd xmm2, xmm3
	movapd xmmword ptr [rsp + 32], xmm2
		// kurbo/src/affine.rs:407
		let angle = 0.5 * (2.0 * (ab + cd)).atan2(a2 - b2 + c2 - d2);
	movapd xmm1, xmm3
	subsd xmm1, xmm2
	movapd xmmword ptr [rsp + 16], xmm4
	addsd xmm1, xmm4
		// kurbo/src/affine.rs:408
		let s1 = a2 + b2 + c2 + d2;
	unpckhpd xmm4, xmm4
	movapd xmmword ptr [rsp], xmm4
		// kurbo/src/affine.rs:407
		let angle = 0.5 * (2.0 * (ab + cd)).atan2(a2 - b2 + c2 - d2);
	subsd xmm1, xmm4
	movapd xmmword ptr [rsp + 64], xmm1
	call qword ptr [rip + atan2@GOTPCREL]
	movapd xmm0, xmmword ptr [rsp + 32]
		// kurbo/src/affine.rs:408
		let s1 = a2 + b2 + c2 + d2;
	addsd xmm0, qword ptr [rsp + 48]
	addsd xmm0, qword ptr [rsp + 16]
	addsd xmm0, qword ptr [rsp]
	movapd xmm1, xmm0
	movapd xmm0, xmmword ptr [rsp + 80]
	mulsd xmm0, xmm0
		// kurbo/src/affine.rs:409
		let s2 = ((a2 - b2 + c2 - d2).powi(2) + 4.0 * (ab + cd).powi(2)).sqrt();
	mulsd xmm0, qword ptr [rip + .LCPI116_0]
	movapd xmm2, xmmword ptr [rsp + 64]
	mulsd xmm2, xmm2
		// kurbo/src/affine.rs:409
		let s2 = ((a2 - b2 + c2 - d2).powi(2) + 4.0 * (ab + cd).powi(2)).sqrt();
	addsd xmm0, xmm2
	sqrtsd xmm0, xmm0
		// kurbo/src/affine.rs:412
		x: (0.5 * (s1 + s2)).sqrt(),
	addsd xmm0, xmm1
	mulsd xmm0, qword ptr [rip + .LCPI116_1]
	sqrtsd xmm0, xmm0
		// kurbo/src/ellipse.rs:134
		}
	add rsp, 104
	.cfi_def_cfa_offset 8
	ret
```

</details>
flaviotore added a commit to flaviotore/kurbo that referenced this pull request Oct 9, 2025
This adds methods to `Ellipse` to get the major and minor radii.

This documents the (private) `Affine::svd` method to guarantee the part
of its behavior we're using here for efficiency.

The `Ellipse::radii()` method effectively already allows for this, by
taking the `x` component for the major radius and the `y` component for
the minor radius; however, we do not currently guarantee that behavior,
and may not want to, to keep the option of changing internal
representations. Plus, within crate boundaries and the specified
inlining, the compiler should be able to eliminate the dead singular
value decomposition code.

Quickly looking over the assembly, there are just the two `sqrtsd`
instructions as expected.

Like linebender/kurbo#496, this is motivated by
linebender/vello#1180, to allow eliding more
computations.

<details>
<summary>x86 assembly</summary>

```assembly
		// kurbo/src/ellipse.rs:132
		pub fn major_radius(&self) -> f64 {
	.cfi_startproc
	sub rsp, 104
	.cfi_def_cfa_offset 112
		// kurbo/src/ellipse.rs:133
		self.inner.svd().0.x
	movupd xmm0, xmmword ptr [rdi]
	movupd xmm4, xmmword ptr [rdi + 16]
		// kurbo/src/affine.rs:401
		let a2 = a * a;
	movapd xmm3, xmm0
	mulpd xmm3, xmm0
		// kurbo/src/affine.rs:405
		let ab = a * b;
	movapd xmm2, xmm0
	unpcklpd xmm2, xmm4
	unpckhpd xmm0, xmm4
		// kurbo/src/affine.rs:403
		let c2 = c * c;
	mulpd xmm4, xmm4
		// kurbo/src/affine.rs:405
		let ab = a * b;
	mulpd xmm0, xmm2
		// kurbo/src/affine.rs:407
		let angle = 0.5 * (2.0 * (ab + cd)).atan2(a2 - b2 + c2 - d2);
	movapd xmm1, xmm0
	unpckhpd xmm1, xmm0
	addsd xmm1, xmm0
	movapd xmmword ptr [rsp + 80], xmm1
	movapd xmm0, xmm1
	addsd xmm0, xmm1
	movapd xmmword ptr [rsp + 48], xmm3
		// kurbo/src/affine.rs:408
		let s1 = a2 + b2 + c2 + d2;
	movapd xmm2, xmm3
	unpckhpd xmm2, xmm3
	movapd xmmword ptr [rsp + 32], xmm2
		// kurbo/src/affine.rs:407
		let angle = 0.5 * (2.0 * (ab + cd)).atan2(a2 - b2 + c2 - d2);
	movapd xmm1, xmm3
	subsd xmm1, xmm2
	movapd xmmword ptr [rsp + 16], xmm4
	addsd xmm1, xmm4
		// kurbo/src/affine.rs:408
		let s1 = a2 + b2 + c2 + d2;
	unpckhpd xmm4, xmm4
	movapd xmmword ptr [rsp], xmm4
		// kurbo/src/affine.rs:407
		let angle = 0.5 * (2.0 * (ab + cd)).atan2(a2 - b2 + c2 - d2);
	subsd xmm1, xmm4
	movapd xmmword ptr [rsp + 64], xmm1
	call qword ptr [rip + atan2@GOTPCREL]
	movapd xmm0, xmmword ptr [rsp + 32]
		// kurbo/src/affine.rs:408
		let s1 = a2 + b2 + c2 + d2;
	addsd xmm0, qword ptr [rsp + 48]
	addsd xmm0, qword ptr [rsp + 16]
	addsd xmm0, qword ptr [rsp]
	movapd xmm1, xmm0
	movapd xmm0, xmmword ptr [rsp + 80]
	mulsd xmm0, xmm0
		// kurbo/src/affine.rs:409
		let s2 = ((a2 - b2 + c2 - d2).powi(2) + 4.0 * (ab + cd).powi(2)).sqrt();
	mulsd xmm0, qword ptr [rip + .LCPI116_0]
	movapd xmm2, xmmword ptr [rsp + 64]
	mulsd xmm2, xmm2
		// kurbo/src/affine.rs:409
		let s2 = ((a2 - b2 + c2 - d2).powi(2) + 4.0 * (ab + cd).powi(2)).sqrt();
	addsd xmm0, xmm2
	sqrtsd xmm0, xmm0
		// kurbo/src/affine.rs:412
		x: (0.5 * (s1 + s2)).sqrt(),
	addsd xmm0, xmm1
	mulsd xmm0, qword ptr [rip + .LCPI116_1]
	sqrtsd xmm0, xmm0
		// kurbo/src/ellipse.rs:134
		}
	add rsp, 104
	.cfi_def_cfa_offset 8
	ret
```

</details>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants