I'm struggling a bit with adopting swift concurrency in my app (a voxel-based 3d sculpting app). I have a class which manages the GPU resources for a sculpture which is approximately:
@MainActor
class GPUManager {
/// Stream data into GPU memory.
func load(blockStream: AsyncStream<VoxelDataChunk>) async { ... }
/// Returns false if a sculpting operation cancelled the save.
func save(callback: (VoxelDataChunk) -> Void) async -> Bool { ... }
/// Render the model using Metal
func render(queue: MTLCommandBuffer, ...) { ... }
/// Apply a sculpting operation. Cancels any in-flight saves.
func sculpt(sculptInfo: SculptInfo) { ... }
}
The problem I'm trying to solve is to not block the main thread while loading or saving voxel data, which takes some time. Previously I had been using internal operation queues (for the saves, so they could be cancelled) and a dispatch queue to protect the data structures from races. The old code is tricky and I was hoping async/await would simplify things.
I tried making this class into an actor
, but quickly ran into the problem that MTKView
expects rendering to happen synchronously. In general I've had trouble adopting async/await because the proliferation of async calls eventually hits some Apple API I don't have control over (such as MTKView
or SwiftUI's FileDocument
).
Any suggestions appreciated!