Download

Avoid blocking game thread when saving file

I’m working on a gearvr app (Unreal 4.14) and trying to download an mp4 (aprox 250mb) and save it to the device.

It seems the save operation blocks the game thread, which causes the rendering to freeze for a few seconds, while the gearvr IMU remains active. The result is that the game freezes and you basically end up being able to look around a frozen quad :confused:

My original download and save code was:


request->OnProcessRequestComplete().BindLambda(&](FHttpRequestPtr request, FHttpResponsePtr response, bool success) {
    		if (!success) {
    			return;
    		}
    
    		TArray<uint8> data = response->GetContent();
    		FString path = FPaths::Combine(FPaths::GamePersistentDownloadDir(), "video.mp4");
    		FBufferArchive archive;
    		archive << data;
    
    		success = FFileHelper::SaveArrayToFile(archive, *path);
    
    		if (success) {
    			UGameplayStatics::OpenLevel(GetWorld(), "Player");
    		}
    	});


Since the downloading operation itself does not seem to be the one blocking the UI, I assumed it would be either the insertion operator (archive << data) or SaveArrayToFile.
So I tried doing both operations on a separate thread, like so:


ACard::ACard() {
    	PrimaryActorTick.bCanEverTick = true;
    	
    	// ...
    	
    	request->OnProcessRequestComplete().BindLambda(&](FHttpRequestPtr request, FHttpResponsePtr response, bool success) {
    		GEngine->AddOnScreenDebugMessage(-1, 2.f,
    			success ? FColor::Green : FColor::Red,
    			success ? TEXT("download successful") : TEXT("download failed"));
    
    		if (!success) {
    			return;
    		}
    
    		task = Task::dispatch([response]() {
    			FBufferArchive archive;
    			TArray<uint8> data = response->GetContent();
    			archive << data;
    
    			FString path = FPaths::Combine(FPaths::GamePersistentDownloadDir(), "video.mp4");
    			FFileHelper::SaveArrayToFile(archive, *path);
    		});
    
    		SetActorTickEnabled(true);
    	});
    }
    
    void ACard::BeginPlay() {
    	Super::BeginPlay();
    	SetActorTickEnabled(false);
    }
    
    void ACard::Tick( float DeltaTime ) {
    	Super::Tick( DeltaTime );
    
    	if (task && task->complete) {
    		SetActorTickEnabled(false);
    		UGameplayStatics::OpenLevel(GetWorld(), "Player");
    	}
    }


Where Task is defined like so:


class  Task : public FRunnable {
    
    private:
    	TFunction<void()> task;
    	FRunnableThread* thread;
    
    public:
    	bool complete{ false };
    
    	static Task* dispatch(TFunction<void()> task) {
    		auto newInstance = new Task(task);
    
    		newInstance->thread = FRunnableThread::Create(
    			newInstance,
    			TEXT(""),
    			0,
    			TPri_BelowNormal
    		);
    
    		return newInstance;
    	}
    
    	Task(TFunction<void()> task) : task(task) {}
    	bool Init() override { return true; }
    	void Stop() override {}
    
    	uint32 Run() override {
    		task();
    		complete = true;
    		return 0;
    	}
    };


But the above seems to make no difference: Saving the file still blocks the UI for about 3secs.
So my question is: how should I be saving the file to storage without blocking the game itself?