1
\$\begingroup\$

I'm working on a gearvr app 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 :/

My download and save code is:

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 assume 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?

UPDATE:

This issue is still not resolved. Following the advice in one of the comments below, I added logs to check that the code does indeed execute on separate threads and it does. The logs are consistent with the actions:

  1. Dispatch save task from game thread.
  2. execute task (save) in background thread.
  3. dispatch new task to load the next level from background thread.
  4. execute new task (load next level) from game thread.

Yet the UI thread still blocks.

01 - 18 19:59 : 10.404 18059 18075 D UE4 : LogTemp:Warning: Dispatch TASK : tID 18075 | is game ? 1
01 - 18 19 : 59 : 10.404 18059 18075 D UE4 : [2017.01.19 - 00.59.10:415][-8608252]LogTemp : Warning : Dispatch TASK : tID 18075 | is game ? 1
01 - 18 19 : 59 : 10.414 18059 18259 D UE4 : LogTemp:Warning: Execute TASK : tID 18259 | is game ? 0
01 - 18 19 : 59 : 10.414 18059 18075 D UE4 : [2017.01.19 - 00.59.10:419][-8608252]LogTemp : Warning : Execute TASK : tID 18259 | is game ? 0
01 - 18 19 : 59 : 10.524 18059 18140 I OVR : PhoneManagerThread - phone running!nfds = 1
01 - 18 19 : 59 : 10.524 18059 18259 D UE4 : LogTemp:Warning: Dispatch TASK : tID 18259 | is game ? 0
01 - 18 19 : 59 : 10.524 18059 18075 D UE4 : [2017.01.19 - 00.59.10:536][-8608252]LogTemp : Warning : Dispatch TASK : tID 18259 | is game ? 0
01 - 18 19 : 59 : 11.064 18059 18075 D UE4 : LogTemp:Warning: Execute TASK : tID 18075 | is game ? 1
01 - 18 19 : 59 : 11.064 18059 18075 D UE4 : [2017.01.19 - 00.59.11:072][-8608252]LogTemp : Warning : Execute TASK : tID 18075 | is game ? 1
\$\endgroup\$
1
\$\begingroup\$

You do not seem to use the UGameplayStatics::SaveGameToSlo that is provided by Unreal Editor 4 to create your save. I also use JSON serialization, in order to have a completely asynchronous and multi-platform save system.

I first followed this example provided by Epic. When it worked well, I decided to put this in a task, by creating a class that inherits from FRunnable.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the info. Thing is, this is a binary file downloaded from a server, completely separate from UE's save system (other than for using the archive buffer). There is no JSON involved either. This is why I did not think of using save slots at all. Have you tried your approach with arbitrary binary data? \$\endgroup\$ – SaldaVonSchwartz Dec 7 '16 at 11:29
  • \$\begingroup\$ No I didn't... By the way, I don't see why your solution, once threaded, still makes your UI freezes. \$\endgroup\$ – Stud Dec 7 '16 at 11:33
  • \$\begingroup\$ neither do I :/ Unless it is not really running on a separate thread, which I have not yet attempted to check. But assuming I am using Runnables the right way, it should be. I even tried with UE's task graph (a bit friendlier than the runnables) and I get the same behavior: the UI freezes for about 3 sec when the file is saved to storage. \$\endgroup\$ – SaldaVonSchwartz Dec 7 '16 at 13:32
  • \$\begingroup\$ Did you add breakpoints to check when it freezes exactly? \$\endgroup\$ – Stud Dec 7 '16 at 16:08
  • \$\begingroup\$ I haven't been able to solve this as of now. And I checked and indeed the code executes on separate threads. \$\endgroup\$ – SaldaVonSchwartz Jan 19 '17 at 1:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for?Browse other questions tagged or ask your own question.