Various low-level optimizations
- In Draggable2D, use unaryMinus instead of times operator, it will be much
more efficient since we don't need to unpack/pack x and y.
- In Offset, rewrite isValid() to avoid using a large constant which saves
4 instructions (11 -> 7) on aarch64. Thanks to Jake Wharton for the idea.
- in Float16, rewrite toBits() and sign to save a bunch of instructions.
Also removes unused constants/methods in Draggable2D and Constraints.
Relnote: N/A
Test: OffsetTest
Change-Id: I5a1ef153b05a4dba6bc4c390d133a6a891642ead
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable2D.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable2D.kt
index 23f3458..6c148a0 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable2D.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/Draggable2D.kt
@@ -31,7 +31,6 @@
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.unit.Velocity
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
/**
@@ -303,9 +302,8 @@
)
}
- private fun Velocity.reverseIfNeeded() = if (reverseDirection) this * -1f else this * 1f
-
- private fun Offset.reverseIfNeeded() = if (reverseDirection) this * -1f else this * 1f
+ @Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress")
+ private inline fun Offset.reverseIfNeeded() = if (reverseDirection) -this else this
}
private class DefaultDraggable2DState(val onDelta: (Offset) -> Unit) : Draggable2DState {
@@ -326,7 +324,5 @@
}
}
-private val NoOpOnDragStarted: suspend CoroutineScope.(startedPosition: Offset) -> Unit = {}
private val NoOpOnDragStart: (startedPosition: Offset) -> Unit = {}
-private val NoOpOnDragStopped: suspend CoroutineScope.(velocity: Velocity) -> Unit = {}
private val NoOpOnDragStop: (velocity: Velocity) -> Unit = {}
diff --git a/compose/ui/ui-geometry/api/restricted_current.txt b/compose/ui/ui-geometry/api/restricted_current.txt
index 13de29e..c1b24e8 100644
--- a/compose/ui/ui-geometry/api/restricted_current.txt
+++ b/compose/ui/ui-geometry/api/restricted_current.txt
@@ -36,6 +36,7 @@
field @kotlin.PublishedApi internal static final long DualFirstNaN = 9187343246269874177L; // 0x7f8000017f800001L
field @kotlin.PublishedApi internal static final long DualFloatInfinityBase = 9187343241974906880L; // 0x7f8000007f800000L
field @kotlin.PublishedApi internal static final long DualFloatSignBit = -9223372034707292160L; // 0x8000000080000000L
+ field @kotlin.PublishedApi internal static final long DualLoadedSignificand = 36028792732385279L; // 0x7fffff007fffffL
field @kotlin.PublishedApi internal static final long DualUnsignedFloatMask = 9223372034707292159L; // 0x7fffffff7fffffffL
field @kotlin.PublishedApi internal static final int FloatInfinityBase = 2139095040; // 0x7f800000
field @kotlin.PublishedApi internal static final long Uint64High32 = -9223372034707292160L; // 0x8000000080000000L
diff --git a/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/OffsetTest.kt b/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/OffsetTest.kt
index 6bcd179..cacc898 100644
--- a/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/OffsetTest.kt
+++ b/compose/ui/ui-geometry/src/androidUnitTest/kotlin/androidx/compose/ui/geometry/OffsetTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:Suppress("RedundantSuppression")
+
package androidx.compose.ui.geometry
import androidx.compose.ui.util.floatFromBits
@@ -57,7 +59,7 @@
try {
Offset.Unspecified.x
Assert.fail("Offset.Unspecified.x is not allowed")
- } catch (t: Throwable) {
+ } catch (_: Throwable) {
// no-op
}
}
@@ -67,7 +69,7 @@
try {
Offset.Unspecified.y
Assert.fail("Offset.Unspecified.y is not allowed")
- } catch (t: Throwable) {
+ } catch (_: Throwable) {
// no-op
}
}
@@ -78,7 +80,7 @@
Offset.Unspecified.copy(x = 100f)
Offset.Unspecified.copy(y = 70f)
Assert.fail("Offset.Unspecified.copy is not allowed")
- } catch (t: Throwable) {
+ } catch (_: Throwable) {
// no-op
}
}
@@ -88,7 +90,7 @@
try {
val (_, _) = Offset.Unspecified
Assert.fail("Size.Unspecified component assignment is not allowed")
- } catch (t: Throwable) {
+ } catch (_: Throwable) {
// no-op
}
}
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/InlineClassHelper.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/InlineClassHelper.kt
index 8338bd2..55e3463 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/InlineClassHelper.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/InlineClassHelper.kt
@@ -40,3 +40,6 @@
// Encodes the first valid NaN in each of the 32 bit chunk of a 64 bit word
@PublishedApi internal const val DualFirstNaN = 0x7f800001_7f800001L
+
+// Set all the significand bits for each 32 bit chunk in a 64 bit word
+@PublishedApi internal const val DualLoadedSignificand = 0x007fffff_007fffffL
diff --git a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
index 100212d..5eca5f4 100644
--- a/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
+++ b/compose/ui/ui-geometry/src/commonMain/kotlin/androidx/compose/ui/geometry/Offset.kt
@@ -107,9 +107,9 @@
*/
@Stable
inline fun isValid(): Boolean {
- // Take the unsigned packed floats and see if they are < InfinityBase + 1 (first NaN)
+ // Take the unsigned packed floats and see if they are > InfinityBase (any NaN)
val v = packedValue and DualUnsignedFloatMask
- return (v - DualFirstNaN) and Uint64High32 == Uint64High32
+ return (v + DualLoadedSignificand) and Uint64High32 == 0L
}
/**
diff --git a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Float16.kt b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Float16.kt
index 24e2318..0914fba 100644
--- a/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Float16.kt
+++ b/compose/ui/ui-graphics/src/commonMain/kotlin/androidx/compose/ui/graphics/Float16.kt
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+@file:Suppress("KotlinRedundantDiagnosticSuppress")
+
package androidx.compose.ui.graphics
import androidx.compose.ui.util.floatFromBits
@@ -153,7 +155,7 @@
*/
fun toBits(): Int =
if (isNaN()) {
- NaN.halfValue.toInt()
+ Fp16TheNaN
} else {
halfValue.toInt() and 0xffff
}
@@ -206,13 +208,16 @@
* * `NaN.sign` is `NaN`
*/
val sign: Float16
- get() =
- when {
- isNaN() -> NaN
- this < NegativeZero -> NegativeOne
- this > PositiveZero -> One
- else -> this // this is zero, either positive or negative
- }
+ get() {
+ val v = halfValue.toInt() and Fp16Combined
+ val u =
+ if ((v > Fp16ExponentMax) or (v == 0)) { // 0.0 or NaN
+ v
+ } else {
+ (halfValue.toInt() and Fp16SignMask) or Fp16One
+ }
+ return Float16(u.toShort())
+ }
/** Returns a [Float16] with the magnitude of this and the sign of [sign] */
fun withSign(sign: Float16): Float16 =
@@ -394,8 +399,8 @@
* @return True if the value is normalized, false otherwise
*/
fun isNormalized(): Boolean {
- return halfValue.toInt() and Fp16ExponentMax != 0 &&
- halfValue.toInt() and Fp16ExponentMax != Fp16ExponentMax
+ val v = halfValue.toInt() and Fp16ExponentMax
+ return (v != 0) and (v != Fp16ExponentMax)
}
/**
@@ -491,9 +496,6 @@
}
}
-private val One = Float16(1f)
-private val NegativeOne = Float16(-1f)
-
private const val Fp16SignShift = 15
private const val Fp16SignMask = 0x8000
private const val Fp16ExponentShift = 10
@@ -502,6 +504,8 @@
private const val Fp16ExponentBias = 15
private const val Fp16Combined = 0x7fff
private const val Fp16ExponentMax = 0x7c00
+private const val Fp16One = 0x3c00
+private const val Fp16TheNaN = 0x7e00
private const val Fp32SignShift = 31
private const val Fp32ExponentShift = 23
diff --git a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
index e1b4315..79ba2da 100644
--- a/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
+++ b/compose/ui/ui-unit/src/commonMain/kotlin/androidx/compose/ui/unit/Constraints.kt
@@ -20,7 +20,6 @@
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
-import androidx.compose.ui.unit.Constraints.Companion.Infinity
import androidx.compose.ui.util.fastCoerceAtLeast
import androidx.compose.ui.util.fastCoerceIn
import kotlin.jvm.JvmInline
@@ -351,40 +350,17 @@
private const val Infinity = Int.MAX_VALUE
/**
- * The bit distribution when the focus of the bits should be on the width, but only a minimal
- * difference in focus.
- *
- * 16 bits assigned to width, 15 bits assigned to height.
- */
-private const val MinFocusWidth = 0x2
-
-/**
- * The bit distribution when the focus of the bits should be on the width, and a maximal number of
- * bits assigned to the width.
- *
- * 18 bits assigned to width, 13 bits assigned to height.
- */
-private const val MaxFocusWidth = 0x3
-
-/**
- * The bit distribution when the focus of the bits should be on the height, but only a minimal
- * difference in focus.
- *
- * 15 bits assigned to width, 16 bits assigned to height.
- */
-private const val MinFocusHeight = 0x1
-
-/**
- * The bit distribution when the focus of the bits should be on the height, and a a maximal number
- * of bits assigned to the height.
- *
- * 13 bits assigned to width, 18 bits assigned to height.
- */
-private const val MaxFocusHeight = 0x0
-
-/**
- * The mask to retrieve the focus ([MinFocusWidth], [MaxFocusWidth], [MinFocusHeight],
- * [MaxFocusHeight]).
+ * The mask to retrieve the focus:
+ * - MaxFocusHeight = 0x0. The bit distribution when the focus of the bits should be on the height,
+ * and a a maximal number of bits assigned to the height. 13 bits assigned to width, 18 bits
+ * assigned to height.
+ * - MinFocusHeight = 0x1. The bit distribution when the focus of the bits should be on the height,
+ * but only a minimal difference in focus. 15 bits assigned to width, 16 bits assigned to height.
+ * - MinFocusWidth = 0x2. The bit distribution when the focus of the bits should be on the width,
+ * but only a minimal difference in focus. 16 bits assigned to width, 15 bits assigned to height.
+ * - MaxFocusWidth = 0x3 .The bit distribution when the focus of the bits should be on the width,
+ * and a maximal number of bits assigned to the width. 18 bits assigned to width, 13 bits assigned
+ * to height.
*/
private const val FocusMask = 0x3L