-2

I have a UIViewRepresentable view that is put up by SwiftUI, in which I create three or four fairly complex graphic objects, and move them within the view at a precise speed. I use a CADisplayLink to calculate how much these objects should move for every new frame.

My frame rate wasn't stellar, so I thought that I would try to cache chunks of the image by creating a UIImage programatically, and caching it in main memory. This works well enough, and I can create new UIImage objects in a separate thread, but the frame rate is actually a bit slower than just drawing everything as it's needed.

The view contains three or four of these things, and I have a background task that creates the UIIMages that will be required in the near future, and re-uses UIImages that have scrolled off the screen, so my memory utilization is relatively minimal - if necessary, I could create one big composite image that just gets scrolled around programatically using a ScrollView, but I am concerned that this approach would use significantly more memory.

Is there a better way to cache reusable chunks of a view and animate them across the view? I would like to offload as much as possible to the GPU, of course. Is creating a UIImage the right approach? Would I be better off creating a CALayer, or some other bitmap representation? Should I put these things into a ScrollView? Should I be creating a view that can be animated by the system instead of animating it frame by frame using CADisplayLink timing? (I need to be able to cancel the animation at a moment's notice).

I appreciate that this might not be a simple code issue that only has one correct answer, but I'd appreciate a push in the right direction, thanks!

Andromeda
  • 551
  • 1
  • 6
  • 15

1 Answers1

-1

Apple has developed a variety of frameworks, each of which might be an appropriate choice for this application. One of the important choices for a software designer is to select the appropriate framework for a particular application.

Apple tends to document individual frameworks without discussing how they relate to other frameworks, which is why I see it as being valuable to consult the development community at large when selecting an architecture.

In this case, some bitmap representations are cached in main memory, some in the video memory of a graphics card (Mac OS only), and some in a mobile devices' backing store for a view. It's not always obvious from Apple's documentation how a particular class is actually implemented.

So, I benchmarked a couple of different approaches (animating the same content of course):

  • creating an array of UIImage objects in memory, and drawing them via CADisplayLink yielded frame rates of about 6 Hz.
  • creating a CALayer and positioning it using CADisplayLink yielded frame rates of 120 Hz.

I don't claim to know how UIImage rasterization is handled, but I'm going to guess that it happens using a main CPU processor and main memory, and that images are copied into graphics memory in the main drawing loop. Obviously some of these tasks are passed to Metal, but it's not obvious how it's actually implemented.

I'm going to guess that CALayers are considered to not need scaling and rasterization, and that viewport changes are handled by the GPU with little CPU intervention.

I still don't know if I'd be better off starting an animation and adjusting it if necessary, or if explicitly positioning the CALayer is best. But the approach I'm using yields good frame rates and low CPU usage, so I'll leave that as an academic question for the reader.

I found a few helpful articles on the subject:

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreAnimation_guide/CoreAnimationBasics/CoreAnimationBasics.html#//apple_ref/doc/uid/TP40004514-CH2-SW3

https://blog.spacemanlabs.com/2011/07/calayers-v-cglayers-or-which-layer-player/

add UIImage in CALayer

https://www.raywenderlich.com/10317653-calayer-tutorial-for-ios-getting-started

Andromeda
  • 551
  • 1
  • 6
  • 15