Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8301283

Util methods for computing text/width height giving up some performance

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P4 P4
    • tbd
    • jfx20
    • javafx
    • None

      Working on something un-related I noticed that PrismTextLayout was getting cache misses even
      when it was invoked twice in a row with the same conditions (text, font, alignment ...)
      The calls came from methods in javafx.controls Utils class which were in turn called from
      methods calculating the preferred and minimum width of a label

      < at javafx.controls@20-internal/javafx.scene.control.skin.LabeledSkinBase.computePrefWidth(LabeledSkinBase.java:329)
      < at javafx.controls@20-internal/javafx.scene.control.Control.computePrefWidth(Control.java:571)
      < at javafx.graphics@20-internal/javafx.scene.Parent.prefWidth(Parent.java:1020)
      < at javafx.graphics@20-internal/javafx.scene.layout.Region.prefWidth(Region.java:1589)
      ---
      > at javafx.controls@20-internal/javafx.scene.control.skin.LabeledSkinBase.computeMinLabeledPartWidth(LabeledSkinBase.java:795)
      > at javafx.controls@20-internal/javafx.scene.control.skin.LabeledSkinBase.computeMinWidth(LabeledSkinBase.java:306)
      > at javafx.controls@20-internal/javafx.scene.control.Control.computeMinWidth(Control.java:504)
      > at javafx.graphics@20-internal/javafx.scene.Parent.minWidth(Parent.java:1048)
      > at javafx.graphics@20-internal/javafx.scene.layout.Region.minWidth(Region.java:1553)


      It seems to be because PrismTextLayout has this test ..
          private boolean copyCache() {
              int align = flags & ALIGN_MASK;
              int boundsType = flags & BOUNDS_MASK;
              /* Caching for boundsType == Center, bias towards Modena */
              return wrapWidth != 0 || align != ALIGN_LEFT || boundsType == 0 || isMirrored();
          }

      and if it return true it doesn't cache.

      It returns true because nothing in the Urils code set the boundsType bit.
      You can see the comment that it wants to cache for Center as used by Modena (supposedly).

      There's then a calculation of the width of "..." then it goes back to the original string to get the height.
      This is all very inefficient on the part of LabeledSkin, you'd think it could ask for this ONCE and then
      cache it itself. PrismTextLayout can only do so much guessing ..

      The interesting thing about Utils.computeTextHeight() is that it does :-
       if (boundsType == TextBoundsType.LOGICAL_VERTICAL_CENTER) {
                  layout.setBoundsType(TextLayout.BOUNDS_CENTER);
              } else {
                  layout.setBoundsType(0);
              }

      So now bounds type is set ! And nothing ever unsets it !
      So now, and only now, will later calls - including back to computeTextWidth() get cache hits.

      The bounds bit only affects the vertical bounds (as Utils apparently knows) so it ignores
      it when getting width .. but this means if the code just called getBounds() and then cached
      that it would probably not have what it wanted for height if it hasn't set that bit yet.
      It seems to me that setting it for width anyway would be better for caching and hence performance.
      This requires updating the method signature and code that calls it.

      I also might want to question the assumption in PrismTextLayout about refusing to cache
      the non center case but I don't have data on it, but if the caller only sets it on the 3rd run
      through we do lose performance.
      Also disruptive to performance is that the getting the width of the ellipsis is actually the 3rd call
      so you'd only use the cache once, blow it with the ellipsis and only later get it filled and re-used

      To be fair, I then see a number of cache hits .. but the test app only has one Label .. adding a 2nd
      then calls for each Label get intermingled so throwing away cache hits has more of an effect.

      So
      1) Always set the vertical bounds type - which *might* in turn mean that PrismTextLayout
      can stop refusing to cache non-centered bounds, but if Modena always sets that, it matters less.
      2) LabeledSkin could consider doing some higher level caching of the bounds or re-order
          the calls so asking for ellipsis bounds doesn't blow the cache.

            Unassigned Unassigned
            prr Philip Race
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: