 		
 		effectRenderer = EffectRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
 		debugRenderer = DebugRenderer(withDevice: device, pixelFormat: view.colorPixelFormat)
+		
+		frameSemaphore = DispatchSemaphore(value: 1)
 	}
 	
 	func updateProjection(viewSize: CGSize, mapSize: CGSize, centeredOn offset: CGPoint, zoomedTo zoom: Float) {

	func makeRenderPass(_ buffer: MTLCommandBuffer, _ renderPass: MTLRenderPassDescriptor, render: @escaping (MTLRenderCommandEncoder) -> ()) -> MapRenderPass {
		return {
			guard let encoder = buffer.makeRenderCommandEncoder(descriptor: renderPass) else { return }
			render(encoder)
			encoder.endEncoding()
			buffer.commit()

	}
	
	func prepareFrame(forWorld worldState: RuntimeWorld) {
		frameSemaphore.wait()
		frameId += 1
		
		let available = AppDelegate.sharedUserState.availableSet
		let visible = AppDelegate.sharedUIState.visibleRegionHashes
		let renderSet = available.intersection(visible)
