Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Atlas Api performs worse than Picture Api #2688

Open
itsramiel opened this issue Oct 13, 2024 · 0 comments
Open

Atlas Api performs worse than Picture Api #2688

itsramiel opened this issue Oct 13, 2024 · 0 comments
Labels
bug Something isn't working

Comments

@itsramiel
Copy link

Description

Hi, first of all thanks for the great library!!

The issue is that Atlas is said to be more efficient at rendering the same instance multiple times but unfortunately that is not the case for me.
For example I tried creating 1000 circles that are falling down, so it is the same image but different transformations and the Picture API while sill has poor performance, was better than Atlas.

Note that I testing on an Android, Samsung Galaxy A34.

So here is an example using Picture:

function App() {
  const size = useSharedValue({width: 0, height: 0});

  const circles = useSharedValue<Array<{x: number; y: number}>>([]);

  useAnimatedReaction(
    () => size.value,
    currentSize => {
      circles.value = Array.from({length: 1000}).map(
        (): SkPoint => ({
          x: getRandomNumber(CIRCLE_RADIUS, currentSize.width - CIRCLE_RADIUS),
          y: getRandomNumber(CIRCLE_RADIUS, currentSize.height - CIRCLE_RADIUS),
        }),
      );
    },
  );

  useFrameCallback(info => {
    if (!info.timeSincePreviousFrame) return;
    const timeSincePreviousFrame = info.timeSincePreviousFrame;

    circles.modify(circles => {
      circles.forEach(circle => {
        circle.y += CIRCLE_SPEED * timeSincePreviousFrame;

        if (circle.y > size.value.height - CIRCLE_RADIUS) {
          circle.y = -CIRCLE_RADIUS;
          circle.x = getRandomNumber(
            CIRCLE_RADIUS,
            size.value.width - CIRCLE_RADIUS,
          );
        }
      });
      return circles;
    });
  });

  const picture = useDerivedValue(() => {
    return createPicture(canvas => {
      const paint = Skia.Paint();

      paint.setColor(Skia.Color('white'));

      circles.value.forEach(circle => {
        canvas.drawCircle(circle.x, circle.y, CIRCLE_RADIUS, paint);
      });
    });
  }, []);

  return (
    <View style={styles.screen}>
      <Canvas onSize={size} style={styles.canvas}>
        <Picture picture={picture} />
      </Canvas>
    </View>
  );
}

And here is the Atlas version:

const image = drawAsImage(
  <Circle cx={0} cy={0} r={CIRCLE_RADIUS} color={'white'} />,
  {
    width: 2 * CIRCLE_RADIUS,
    height: 2 * CIRCLE_RADIUS,
  },
);

const sprites = Array.from({length: CIRCLE_COUNT}).map(
  (): SkRect => rect(0, 0, CIRCLE_RADIUS * 2, CIRCLE_RADIUS * 2),
);

function getRandomNumber(min: number, max: number) {
  'worklet';
  return Math.random() * (max - min) + min;
}

function App() {
  const size = useSharedValue({width: 0, height: 0});

  const circles = useSharedValue<Array<{x: number; y: number}>>([]);

  useAnimatedReaction(
    () => size.value,
    currentSize => {
      circles.value = Array.from({length: 1000}).map(
        (): SkPoint => ({
          x: getRandomNumber(CIRCLE_RADIUS, currentSize.width - CIRCLE_RADIUS),
          y: getRandomNumber(CIRCLE_RADIUS, currentSize.height - CIRCLE_RADIUS),
        }),
      );
    },
  );

  useFrameCallback(info => {
    if (!info.timeSincePreviousFrame) return;
    const timeSincePreviousFrame = info.timeSincePreviousFrame;

    circles.modify(circles => {
      circles.forEach(circle => {
        circle.y += CIRCLE_SPEED * timeSincePreviousFrame;

        if (circle.y > size.value.height - CIRCLE_RADIUS) {
          circle.y = -CIRCLE_RADIUS;
          circle.x = getRandomNumber(
            CIRCLE_RADIUS,
            size.value.width - CIRCLE_RADIUS,
          );
        }
      });
      return circles;
    });
  });

  const transforms = useDerivedValue(() => {
    return circles.value.map(circle => Skia.RSXform(1, 0, circle.x, circle.y));
  }, []);

  return (
    <View style={styles.screen}>
      <Canvas onSize={size} style={styles.canvas}>
        <Atlas image={image} sprites={sprites} transforms={transforms} />
      </Canvas>
    </View>
  );
}
Atlas Picture
Screen_Recording_20241013_194143_PictureVsAtlas.mp4
Screen_Recording_20241013_194351_PictureVsAtlas.mp4

Not sure also why the output looks different, Atlas circles are smaller? But anyways perf is the bug at the moment

Version

1.4.2

Steps to reproduce

Clone the repo provided and checkout the main branch which uses the picture api and the atlas branch which uses the Atlas Api on an Android device? Perhaps a low end if possible eg: Samsung A34

Snack, code example, screenshot, or link to a repository

Picture
Atlas

@itsramiel itsramiel added the bug Something isn't working label Oct 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant