Skip to content

Commit

Permalink
Fix bug of the binary version of FExecStatus.
Browse files Browse the repository at this point in the history
  • Loading branch information
qiuwch committed May 25, 2017
1 parent 04ce134 commit 65430c8
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 109 deletions.
68 changes: 35 additions & 33 deletions Source/UnrealCV/Private/CommandDispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,43 +43,45 @@ class FAsyncWatcher : public FRunnable
FRunnableThread* Thread;
virtual uint32 Run() override
{
/** FIXME: Find a more efficient implementation for this */
while (!Stopping)
{
while (IsActive())
if (!IsActive())
{
SCOPE_CYCLE_COUNTER(STAT_WaitAsync);
FPromise Promise;
PendingPromise.Peek(Promise);
FExecStatus ExecStatus = Promise.CheckStatus();
if (ExecStatus != FExecStatusType::Pending) // Job is finished
{
FPromise CompletedPromise;
FCallbackDelegate CompletedCallback;
PendingPromise.Dequeue(CompletedPromise); // Dequeue in the same thread
PendingCompletedCallback.Dequeue(CompletedCallback);
continue;
}
SCOPE_CYCLE_COUNTER(STAT_WaitAsync);
FPromise Promise;
PendingPromise.Peek(Promise);
FExecStatus ExecStatus = Promise.CheckStatus();
if (ExecStatus != FExecStatusType::Pending) // Job is finished
{
FPromise CompletedPromise;
FCallbackDelegate CompletedCallback;
PendingPromise.Dequeue(CompletedPromise); // Dequeue in the same thread
PendingCompletedCallback.Dequeue(CompletedCallback);

// This needs to be sent back to game thread
AsyncTask(ENamedThreads::GameThread, [CompletedCallback, ExecStatus]() {
CompletedCallback.ExecuteIfBound(ExecStatus);
// CompletedCallback.Unbind();
});
// Stop the timer
}
if (Promise.GetRunningTime() > 5) // Kill pending task that takes too long
{
UE_LOG(LogUnrealCV, Warning, TEXT("An async task failed to finish and timeout after 5 seconds"));
// This is a failed task
FPromise FailedPromise;
FCallbackDelegate CompletedCallback;
PendingPromise.Dequeue(FailedPromise); // Dequeue in the same thread
PendingCompletedCallback.Dequeue(CompletedCallback);
// This needs to be sent back to game thread
AsyncTask(ENamedThreads::GameThread, [CompletedCallback, ExecStatus]() {
CompletedCallback.ExecuteIfBound(ExecStatus);
// CompletedCallback.Unbind();
});
// Stop the timer
}
if (Promise.GetRunningTime() > 5) // Kill pending task that takes too long
{
UE_LOG(LogUnrealCV, Warning, TEXT("An async task failed to finish and timeout after 5 seconds"));
// This is a failed task
FPromise FailedPromise;
FCallbackDelegate CompletedCallback;
PendingPromise.Dequeue(FailedPromise); // Dequeue in the same thread
PendingCompletedCallback.Dequeue(CompletedCallback);

// This needs to be sent back to game thread
AsyncTask(ENamedThreads::GameThread, [CompletedCallback]() {
CompletedCallback.ExecuteIfBound(FExecStatus::Error("Task took too long, timeout"));
// CompletedCallback.Unbind();
});
}
// This needs to be sent back to game thread
AsyncTask(ENamedThreads::GameThread, [CompletedCallback]() {
CompletedCallback.ExecuteIfBound(FExecStatus::Error("Task took too long, timeout"));
// CompletedCallback.Unbind();
});
}
}
return 0; // This thread will exit
Expand Down Expand Up @@ -244,7 +246,7 @@ const TMap<FString, FString>& FCommandDispatcher::GetUriDescription()
}

void FCommandDispatcher::ExecAsync(const FString Uri, const FCallbackDelegate Callback)
// TODO: This is a stupid implementation to use a new thread to check whether the task is completed.
// FIXME: This is a stupid implementation to use a new thread to check whether the task is completed.
// ExecAsync can not guarantee the execuation order, this needs to be enforced in the client.
// Which means later tasks can finish earlier
{
Expand Down
32 changes: 18 additions & 14 deletions Source/UnrealCV/Private/Commands/CameraHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "CaptureManager.h"
#include "CineCameraActor.h"
#include "ObjectPainter.h"
#include "ScreenCapture.h"

FString GetDiskFilename(FString Filename)
{
Expand Down Expand Up @@ -179,7 +180,7 @@ FExecStatus FCameraCommandHandler::GetCameraRotation(const TArray<FString>& Args
ACineCameraActor* CineCameraActor = nullptr;
for (AActor* Actor : this->GetWorld()->GetCurrentLevel()->Actors)
{
// if (Actor && Actor->IsA(AMatineeActor::StaticClass())) // AMatineeActor is deprecated
// if (Actor && Actor->IsA(AMatineeActor::StaticClass())) // AMatineeActor is deprecated
if (Actor && Actor->IsA(ACineCameraActor::StaticClass()))
{
bIsMatinee = true;
Expand Down Expand Up @@ -212,7 +213,7 @@ FExecStatus FCameraCommandHandler::GetCameraLocation(const TArray<FString>& Args
ACineCameraActor* CineCameraActor = nullptr;
for (AActor* Actor : this->GetWorld()->GetCurrentLevel()->Actors)
{
// if (Actor && Actor->IsA(AMatineeActor::StaticClass())) // AMatineeActor is deprecated
// if (Actor && Actor->IsA(AMatineeActor::StaticClass())) // AMatineeActor is deprecated
if (Actor && Actor->IsA(ACineCameraActor::StaticClass()))
{
bIsMatinee = true;
Expand Down Expand Up @@ -316,30 +317,33 @@ FExecStatus FCameraCommandHandler::GetCameraViewMode(const TArray<FString>& Args
}
});
FString Message = FString::Printf(TEXT("File will be saved to %s"), *Filename);
return FExecStatus::AsyncQuery(FPromise(PromiseDelegate), Message);
return FExecStatus::AsyncQuery(FPromise(PromiseDelegate));
// The filename here is just for message, not the fullname on the disk, because we can not know that due to sandbox issue.
}
return FExecStatus::InvalidArgument;
}

/** vget /camera/[id]/screenshot */
FExecStatus FCameraCommandHandler::GetScreenshot(const TArray<FString>& Args)
{
if (Args.Num() <= 2)
{
int32 CameraId = FCString::Atoi(*Args[0]);
int32 CameraId = FCString::Atoi(*Args[0]);

FString Filename;
if (Args.Num() == 1)
FString Filename;
if (Args.Num() == 1)
{
Filename = GenerateSeqFilename();
}
if (Args.Num() == 2)
{
Filename = Args[1];
if (Filename.ToLower() == TEXT("png"))
{
Filename = GenerateSeqFilename();
return ScreenCaptureAsyncByQuery(); // return the binary data
}
if (Args.Num() == 2)
else
{
Filename = Args[1];
return ScreenCaptureAsyncByQuery(Filename);
}

// FString FullFilename = GetDiskFilename(Filename);
return FScreenCapture::GetCameraViewAsyncQuery(Filename);
}
return FExecStatus::InvalidArgument;
}
Expand Down
6 changes: 0 additions & 6 deletions Source/UnrealCV/Private/Commands/CameraHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@
#include "CommandHandler.h"
#include "GTCaptureComponent.h"

class FScreenCapture
{
public:
static FExecStatus GetCameraViewAsyncQuery(const FString& FullFilename);
};

class FCameraCommandHandler : public FCommandHandler
{
public:
Expand Down
17 changes: 7 additions & 10 deletions Source/UnrealCV/Private/ExecStatus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,8 @@ bool operator!=(const FExecStatus& ExecStatus, const FExecStatusType& ExecStatus

FExecStatus& FExecStatus::operator+=(const FExecStatus& Src)
{
this->BinaryData += Src.BinaryData;
this->MessageBody += "\n" + Src.MessageBody;
if (Src.ExecStatusType == FExecStatusType::OK)
{

}
return *this;
}

Expand All @@ -54,9 +51,9 @@ FExecStatus FExecStatus::Pending(FString InMessage)
return FExecStatus(FExecStatusType::Pending, InMessage);
}

FExecStatus FExecStatus::AsyncQuery(FPromise InPromise, FString InMessage)
FExecStatus FExecStatus::AsyncQuery(FPromise InPromise)
{
return FExecStatus(FExecStatusType::AsyncQuery, InPromise, InMessage);
return FExecStatus(FExecStatusType::AsyncQuery, InPromise);
}

FExecStatus FExecStatus::Error(FString ErrorMessage)
Expand Down Expand Up @@ -87,11 +84,10 @@ FString FExecStatus::GetMessage() const // Define how to format the reply string
return Message;
}

FExecStatus::FExecStatus(FExecStatusType InExecStatusType, FPromise InPromise, FString InMessage)
FExecStatus::FExecStatus(FExecStatusType InExecStatusType, FPromise InPromise)
{
ExecStatusType = InExecStatusType;
Promise = InPromise;
MessageBody = InMessage;
}

FExecStatus::FExecStatus(FExecStatusType InExecStatusType, FString InMessage)
Expand All @@ -109,9 +105,10 @@ FExecStatus FExecStatus::Binary(TArray<uint8>& BinaryData)
return FExecStatus(FExecStatusType::OK, BinaryData);
}

FExecStatus::FExecStatus(FExecStatusType InExecStatusType, TArray<uint8>& BinaryData)
FExecStatus::FExecStatus(FExecStatusType InExecStatusType, TArray<uint8>& InBinaryData)
{
this->BinaryData = BinaryData;
ExecStatusType = InExecStatusType;
BinaryData = InBinaryData;
}

TArray<uint8> FExecStatus::GetData() const // Define how to format the reply string
Expand Down
122 changes: 80 additions & 42 deletions Source/UnrealCV/Private/ScreenCapture.cpp
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
#include "UnrealCVPrivate.h"
#include "Commands/CameraHandler.h"
#include "ScreenCapture.h"
#include "UE4CVServer.h"

/** Deprecated: Get camera view in a sync way, can not work in standalone mode */
// FExecStatus GetCameraViewSync(const FString& Fullfilename);
/** Get camera view in async way, the return FExecStaus is in pending status and need to check the promise to get result */
// FExecStatus GetCameraViewAsyncQuery(const FString& Fullfilename);
// FExecStatus GetCameraViewAsyncCallback(const FString& Fullfilename);

// Sync operation for screen capture
bool DoCaptureScreen(UGameViewportClient *ViewportClient, const FString& CaptureFilename)
/** Sync operation for screen capture */
bool CaptureWithSync(UGameViewportClient *ViewportClient, const FString& CaptureFilename)
{
bool bScreenshotSuccessful = false;
FViewport* InViewport = ViewportClient->Viewport;
Expand Down Expand Up @@ -57,39 +52,15 @@ bool DoCaptureScreen(UGameViewportClient *ViewportClient, const FString& Capture

extern FString GetDiskFilename(FString Filename);

FExecStatus FScreenCapture::GetCameraViewAsyncQuery(const FString& Filename)
/**
Capture the screen with a customize viewport
*/
void CaptureWithCustomViewport()
{
// Method 1: Use custom ViewportClient
// UMyGameViewportClient* ViewportClient = (UMyGameViewportClient*)Character->GetWorld()->GetGameViewport();
// ViewportClient->CaptureScreen(FullFilename);

// Method2: System screenshot function
const FString Dir = FPlatformProcess::BaseDir(); // TODO: Change this to screen capture folder
FString FullFilename = FPaths::Combine(*Dir, *Filename);

FScreenshotRequest::RequestScreenshot(FullFilename, false, false); // This is an async operation
// It is important to pass in the FullFilename

// Implement 2, Start async and query
// FPromiseDelegate PromiseDelegate = FPromiseDelegate::CreateRaw(this, &FCameraCommandHandler::CheckStatusScreenshot);
FPromiseDelegate PromiseDelegate = FPromiseDelegate::CreateLambda([FullFilename]()
{
if (FScreenshotRequest::IsScreenshotRequested())
{
return FExecStatus::Pending();
}
else
{
FString DiskFilename = IFileManager::Get().GetFilenameOnDisk(*FullFilename); // This is important
// FString DiskFilename = IFileManager::Get().GetFilenameOnDisk(*FullFilename); // This is important
// See: https://wiki.unrealengine.com/Packaged_Game_Paths,_Obtain_Directories_Based_on_Executable_Location.
return FExecStatus::OK(DiskFilename);
}
});

// Method3: USceneCaptureComponent2D, inspired by StereoPanorama plugin

FExecStatus ExecStatusQuery = FExecStatus::AsyncQuery(FPromise(PromiseDelegate));
/* This is only valid within custom viewport client
UGameViewportClient* ViewportClient = Character->GetWorld()->GetGameViewport();
ViewportClient->OnScreenshotCaptured().Clear(); // This is required to handle the filename issue.
Expand All @@ -108,11 +79,78 @@ FExecStatus FScreenCapture::GetCameraViewAsyncQuery(const FString& Filename)
FFileHelper::SaveArrayToFile(CompressedBitmap, *FullFilename);
});
*/
return ExecStatusQuery;
}

/**
Capture the screen with the built-in implementation of UE4
*/
FExecStatus CaptureWithBuiltIn(const FString& Filename)
{
// Method2: System screenshot function
const FString Dir = FPlatformProcess::BaseDir(); // TODO: Change this to screen capture folder
FString FullFilename = FPaths::Combine(*Dir, *Filename);

FScreenshotRequest::RequestScreenshot(FullFilename, false, false); // This is an async operation
// It is important to pass in the FullFilename

// Implement 2, Start async and query
FPromiseDelegate PromiseDelegate = FPromiseDelegate::CreateLambda([FullFilename]()
{
if (FScreenshotRequest::IsScreenshotRequested())
{
return FExecStatus::Pending();
}
else
{
FString DiskFilename = IFileManager::Get().GetFilenameOnDisk(*FullFilename); // This is very important
// See: https://wiki.unrealengine.com/Packaged_Game_Paths,_Obtain_Directories_Based_on_Executable_Location.
return FExecStatus::OK(DiskFilename);
}
});

FExecStatus ExecStatusQuery = FExecStatus::AsyncQuery(FPromise(PromiseDelegate));
return ExecStatusQuery;
}



// Method3: USceneCaptureComponent2D, inspired by StereoPanorama plugin
void CaptureWithUSceneCaptureComponent()
{
// See CaptureManager.cpp for more details
}

FExecStatus ScreenCaptureAsyncByQuery(const FString& Filename)
{
return CaptureWithBuiltIn(Filename);
}

FExecStatus ScreenCaptureAsyncByQuery()
{
/* FIXME: This is a hacky way to get binary, better to get rid of saving the file */
const FString Dir = FPlatformProcess::BaseDir();
FString FullFilename = FPaths::Combine(*Dir, TEXT("tmp.png"));
FScreenshotRequest::RequestScreenshot(FullFilename, false, false);

FPromiseDelegate PromiseDelegate = FPromiseDelegate::CreateLambda([FullFilename]()
{
if (FScreenshotRequest::IsScreenshotRequested())
{
return FExecStatus::Pending();
}
else
{
TArray<uint8> Binary;
FFileHelper::LoadFileToArray(Binary, *FullFilename);
return FExecStatus::Binary(Binary);
}
});

FExecStatus ExecStatusQuery = FExecStatus::AsyncQuery(FPromise(PromiseDelegate));
return ExecStatusQuery;
}

/*
/* This is an unfinished implementation
FExecStatus FCameraCommandHandler::GetCameraViewAsyncCallback(const FString& FullFilename)
{
FScreenshotRequest::RequestScreenshot(FullFilename, false, false); // This is an async operation
Expand Down Expand Up @@ -142,14 +180,14 @@ FExecStatus FCameraCommandHandler::GetCameraViewAsyncCallback(const FString& Ful
}
*/

FExecStatus GetCameraViewSync(const FString& FullFilename)
FExecStatus ScreenCaptureSync(const FString& FullFilename)
{
// This can only work within editor
// Warning: This can only work within editor
// Reimplement a GameViewportClient is required according to the discussion from here
// https://forums.unrealengine.com/showthread.php?50857-FViewPort-ReadPixels-crash-while-play-on-quot-standalone-Game-quot-mode
UWorld* World = FUE4CVServer::Get().GetGameWorld();
UGameViewportClient* ViewportClient = World->GetGameViewport();
if (DoCaptureScreen(ViewportClient, FullFilename))
if (CaptureWithSync(ViewportClient, FullFilename))
{
return FExecStatus::OK(FullFilename);
}
Expand Down

0 comments on commit 65430c8

Please sign in to comment.