Files
UnrealEngineUWP/Engine/Source/Runtime/AutomationWorker/Private/AutomationWorkerModule.cpp
Jack Porter 9e86647a3d Copying //UE4/Dev-Mobile to //UE4/Dev-Main (Source: //UE4/Dev-Mobile @ 3010693)
#lockdown nick.penwarden
#rb none

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2958982 on 2016/04/28 by Dmitriy.Dyomin

	Set owner name for RHI texture, for easier debugging

Change 2976446 on 2016/05/12 by Niklas.Smedberg

	Fixed Device Profile CVars so they work even if a DLL with the cvar definition is loaded afterwards. (And they now also go through the common code path for CVars.)

Change 2983781 on 2016/05/19 by Steve.Cano

	Check in PlayUsingLauncher if the device we're launching to is authorized by the computer. Could not get to this information about Devices so added an IsAuthorized interface to ITargetDevice that is overriden in the AndroidTargetDevice. Also make sure to referesh the authorized state as needed for Android device detection. Finally, changed the name of the authorized variable to be more readable (true == authorized instead of true == unauthorized)

	#jira UE-21121
	#ue4
	#android

Change 2994202 on 2016/05/31 by Allan.Bentham

	Prevent clear transulcency volume null deref crash.

	Change test for allocated deferred render targets  by testing against an exclusively deferred target (instead of potentially shared shadow depth surface)

	probable fix for UE-22073

Change 2995613 on 2016/05/31 by Dmitriy.Dyomin

	Added: Option to force full precision in a material
	UEMOB-109

Change 2997960 on 2016/06/02 by Gareth.Martin

	Refactored Landscape serialization to allow cooking both the data used for normal rendering and mobile rendering into the same package
	#jira UE-31474

Change 2997988 on 2016/06/02 by Gareth.Martin

	Files missing from CL 2997960
	#jira UE-31474

Change 2999222 on 2016/06/03 by Jack.Porter

	Fix up ETargetPlatformFeatures::ForwardRendering and ETargetPlatformFeatures::DeferredRendering for iOS to support the Metal MRT deferred renderer

Change 2999229 on 2016/06/03 by Jack.Porter

	Rename ETargetPlatformFeatures::ForwardRendering to TargetPlatformFeatures::MobileRendering

Change 3003540 on 2016/06/07 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3003779 on 2016/06/07 by Dmitriy.Dyomin

	Fixed: Criss-crossed sublevels cause NavMesh errors
	#jira UE-27157

Change 3004535 on 2016/06/07 by Steve.Cano

	Adding the OnControllerConnectionChange delegate message when a controller is connected on Android. Also added additional future broadcast statement when disconnect support is added for Android.

	#jira UE-25697
	#ue4
	#android

Change 3005205 on 2016/06/07 by Niklas.Smedberg

	Bumped ASTC format version to invalidate bad server DDC

Change 3005347 on 2016/06/08 by Dmitriy.Dyomin

	Added a way to cache OpenGL program binaries on the disk. Disabled by default. Can be enabled only on Android platform (r.UseProgramBinaryCache=1)
	#jira UEMOB-108

Change 3005524 on 2016/06/08 by Dmitriy.Dyomin

	Fixed iOS build broken by CL# 3005347

Change 3005528 on 2016/06/08 by Jack.Porter

	Changed hardcoded checkboxes from quality level overrides dialog to use the general property details code.
	Now magically supports any uproperty types such as enums or integers added to FMaterialQualityOverrides.

Change 3005607 on 2016/06/08 by Dmitriy.Dyomin

	Fixed: Occasional crash on using Launch on Android device when device is being disconnected

Change 3006705 on 2016/06/08 by Chris.Babcock

	Fix virtual joystick to return -1 to 1 ranges for thumbsticks
	#jira UE-31799
	#ue4
	#android
	#ios

Change 3006960 on 2016/06/08 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3007050 on 2016/06/09 by Jack.Porter

	FAutomationWorkerModule::ReportTestComplete() needs to send analytics first as the message endpoint will free the memory resulting in a crash

Change 3007129 on 2016/06/09 by Dmitriy.Dyomin

	Fixed: Black edges seen on flames in Sun Temple
	#jira UE-31712

Change 3010686 on 2016/06/13 by Dmitriy.Dyomin

	Fixed: Android Monolithic warnings for glGetProgramBinaryOES and glProgramBinaryOES
	#jira UE-31933

[CL 3011074 by Jack Porter in Main branch]
2016-06-13 12:20:22 -04:00

484 lines
15 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "AutomationWorkerPrivatePCH.h"
#if WITH_EDITOR
#include "AssetRegistryModule.h"
#endif
#define LOCTEXT_NAMESPACE "AutomationTest"
IMPLEMENT_MODULE(FAutomationWorkerModule, AutomationWorker);
/* IModuleInterface interface
*****************************************************************************/
void FAutomationWorkerModule::StartupModule()
{
Initialize();
FAutomationTestFramework::GetInstance().PreTestingEvent.AddRaw(this, &FAutomationWorkerModule::HandlePreTestingEvent);
FAutomationTestFramework::GetInstance().PostTestingEvent.AddRaw(this, &FAutomationWorkerModule::HandlePostTestingEvent);
}
void FAutomationWorkerModule::ShutdownModule()
{
FAutomationTestFramework::GetInstance().PreTestingEvent.RemoveAll(this);
FAutomationTestFramework::GetInstance().PostTestingEvent.RemoveAll(this);
}
bool FAutomationWorkerModule::SupportsDynamicReloading()
{
return true;
}
/* IAutomationWorkerModule interface
*****************************************************************************/
void FAutomationWorkerModule::Tick()
{
//execute latent commands from the previous frame. Gives the rest of the engine a turn to tick before closing the test
bool bAllLatentCommandsComplete = ExecuteLatentCommands();
if (bAllLatentCommandsComplete)
{
//if we were running the latent commands as a result of executing a network command, report that we are now done
if (bExecutingNetworkCommandResults)
{
ReportNetworkCommandComplete();
bExecutingNetworkCommandResults = false;
}
//if the controller has requested the next network command be executed
if (bExecuteNextNetworkCommand)
{
//execute network commands if there are any queued up and our role is appropriate
bool bAllNetworkCommandsComplete = ExecuteNetworkCommands();
if (bAllNetworkCommandsComplete)
{
ReportTestComplete();
}
//we've now executed a network command which may have enqueued further latent actions
bExecutingNetworkCommandResults = true;
//do not execute anything else until expressly told to by the controller
bExecuteNextNetworkCommand = false;
}
}
if (MessageEndpoint.IsValid())
{
MessageEndpoint->ProcessInbox();
}
}
/* ISessionManager implementation
*****************************************************************************/
bool FAutomationWorkerModule::ExecuteLatentCommands()
{
bool bAllLatentCommandsComplete = false;
if (GIsAutomationTesting)
{
// Ensure that latent automation commands have time to execute
bAllLatentCommandsComplete = FAutomationTestFramework::GetInstance().ExecuteLatentCommands();
}
return bAllLatentCommandsComplete;
}
bool FAutomationWorkerModule::ExecuteNetworkCommands()
{
bool bAllLatentCommandsComplete = false;
if (GIsAutomationTesting)
{
// Ensure that latent automation commands have time to execute
bAllLatentCommandsComplete = FAutomationTestFramework::GetInstance().ExecuteNetworkCommands();
}
return bAllLatentCommandsComplete;
}
void FAutomationWorkerModule::Initialize()
{
if (FPlatformProcess::SupportsMultithreading())
{
// initialize messaging
MessageEndpoint = FMessageEndpoint::Builder("FAutomationWorkerModule")
.Handling<FAutomationWorkerFindWorkers>(this, &FAutomationWorkerModule::HandleFindWorkersMessage)
.Handling<FAutomationWorkerNextNetworkCommandReply>(this, &FAutomationWorkerModule::HandleNextNetworkCommandReplyMessage)
.Handling<FAutomationWorkerPing>(this, &FAutomationWorkerModule::HandlePingMessage)
.Handling<FAutomationWorkerResetTests>(this, &FAutomationWorkerModule::HandleResetTests)
.Handling<FAutomationWorkerRequestTests>(this, &FAutomationWorkerModule::HandleRequestTestsMessage)
.Handling<FAutomationWorkerRunTests>(this, &FAutomationWorkerModule::HandleRunTestsMessage)
.WithInbox();
if (MessageEndpoint.IsValid())
{
MessageEndpoint->Subscribe<FAutomationWorkerFindWorkers>();
}
bExecuteNextNetworkCommand = true;
}
else
{
bExecuteNextNetworkCommand = false;
}
ExecutionCount = INDEX_NONE;
bExecutingNetworkCommandResults = false;
bSendAnalytics = false;
}
void FAutomationWorkerModule::ReportNetworkCommandComplete()
{
if (GIsAutomationTesting)
{
MessageEndpoint->Send(new FAutomationWorkerRequestNextNetworkCommand(ExecutionCount), TestRequesterAddress);
if (StopTestEvent.IsBound())
{
// this is a local test; the message to continue will never arrive, so lets not wait for it
bExecuteNextNetworkCommand = true;
}
}
}
/**
* Takes a large transport array and splits it into pieces of a desired size and returns the portion of this which is requested
*
* @param FullTransportArray The whole series of data
* @param CurrentChunkIndex The The chunk we are requesting
* @param NumToSend The maximum number of bytes we should be splitting into.
* @return The section of the transport array which matches our index requested
*/
TArray< uint8 > GetTransportSection( const TArray< uint8 >& FullTransportArray, const int32 NumToSend, const int32 RequestedChunkIndex )
{
TArray< uint8 > TransportArray = FullTransportArray;
if( NumToSend > 0 )
{
int32 NumToRemoveFromStart = RequestedChunkIndex * NumToSend;
if( NumToRemoveFromStart > 0 )
{
TransportArray.RemoveAt( 0, NumToRemoveFromStart );
}
int32 NumToRemoveFromEnd = FullTransportArray.Num() - NumToRemoveFromStart - NumToSend;
if( NumToRemoveFromEnd > 0 )
{
TransportArray.RemoveAt( TransportArray.Num()-NumToRemoveFromEnd, NumToRemoveFromEnd );
}
}
else
{
TransportArray.Empty();
}
return TransportArray;
}
void FAutomationWorkerModule::ReportTestComplete()
{
if (GIsAutomationTesting)
{
//see if there are any more network commands left to execute
bool bAllLatentCommandsComplete = FAutomationTestFramework::GetInstance().ExecuteLatentCommands();
//structure to track error/warning/log messages
FAutomationTestExecutionInfo ExecutionInfo;
bool bSuccess = FAutomationTestFramework::GetInstance().StopTest(ExecutionInfo);
if (StopTestEvent.IsBound())
{
StopTestEvent.Execute(bSuccess, TestName, ExecutionInfo);
}
else
{
// send the results to the controller
FAutomationWorkerRunTestsReply* Message = new FAutomationWorkerRunTestsReply();
Message->TestName = TestName;
Message->ExecutionCount = ExecutionCount;
Message->Success = bSuccess;
Message->Duration = ExecutionInfo.Duration;
Message->Errors = ExecutionInfo.Errors;
Message->Warnings = ExecutionInfo.Warnings;
Message->Logs = ExecutionInfo.LogItems;
// sending though the endpoint will free Message memory, so analytics need to be sent first
if (bSendAnalytics)
{
if (!FAutomationAnalytics::IsInitialized())
{
FAutomationAnalytics::Initialize();
}
FAutomationAnalytics::FireEvent_AutomationTestResults(Message, BeautifiedTestName);
SendAnalyticsEvents(ExecutionInfo.AnalyticsItems);
}
MessageEndpoint->Send(Message, TestRequesterAddress);
}
// reset local state
TestRequesterAddress.Invalidate();
ExecutionCount = INDEX_NONE;
TestName.Empty();
StopTestEvent.Unbind();
}
}
void FAutomationWorkerModule::SendTests( const FMessageAddress& ControllerAddress )
{
for( int32 TestIndex = 0; TestIndex < TestInfo.Num(); TestIndex++ )
{
MessageEndpoint->Send(new FAutomationWorkerRequestTestsReply(TestInfo[TestIndex].GetTestAsString(), TestInfo.Num()), ControllerAddress);
}
MessageEndpoint->Send(new FAutomationWorkerRequestTestsReplyComplete(), ControllerAddress);
}
/* FAutomationWorkerModule callbacks
*****************************************************************************/
void FAutomationWorkerModule::HandleFindWorkersMessage(const FAutomationWorkerFindWorkers& Message, const IMessageContextRef& Context)
{
// Set the Instance name to be the same as the session browser. This information should be shared at some point
if ((Message.SessionId == FApp::GetSessionId()) && (Message.Changelist == 10000))
{
TestRequesterAddress = Context->GetSender();
#if WITH_EDITOR
//If the asset registry is loading assets then we'll wait for it to stop before running our automation tests.
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
if (AssetRegistryModule.Get().IsLoadingAssets())
{
if (!AssetRegistryModule.Get().OnFilesLoaded().IsBoundToObject(this))
{
AssetRegistryModule.Get().OnFilesLoaded().AddRaw(this, &FAutomationWorkerModule::SendWorkerFound);
GLog->Logf(ELogVerbosity::Log, TEXT("...Forcing Asset Registry Load For Automation"));
}
}
else
#endif
{
//If the registry is not loading then we'll just go ahead and run our tests.
SendWorkerFound();
}
}
}
void FAutomationWorkerModule::SendWorkerFound()
{
FAutomationWorkerFindWorkersResponse* Response = new FAutomationWorkerFindWorkersResponse();
FString OSMajorVersionString, OSSubVersionString;
FPlatformMisc::GetOSVersions(OSMajorVersionString, OSSubVersionString);
FString OSVersionString = OSMajorVersionString + TEXT(" ") + OSSubVersionString;
FString CPUModelString = FPlatformMisc::GetCPUBrand().Trim();
Response->DeviceName = FPlatformProcess::ComputerName();
Response->InstanceName = FString::Printf(TEXT("%s-%i"), FPlatformProcess::ComputerName(), FPlatformProcess::GetCurrentProcessId());
Response->Platform = FPlatformProperties::PlatformName();
Response->SessionId = FApp::GetSessionId();
Response->OSVersionName = OSVersionString;
Response->ModelName = FPlatformMisc::GetDefaultDeviceProfileName();
Response->GPUName = FPlatformMisc::GetPrimaryGPUBrand();
Response->CPUModelName = CPUModelString;
Response->RAMInGB = FPlatformMemory::GetPhysicalGBRam();
#if WITH_ENGINE
Response->RenderModeName = AutomationCommon::GetRenderDetailsString();
#else
Response->RenderModeName = TEXT("Unknown");
#endif
MessageEndpoint->Send(Response, TestRequesterAddress);
TestRequesterAddress.Invalidate();
}
void FAutomationWorkerModule::HandleNextNetworkCommandReplyMessage( const FAutomationWorkerNextNetworkCommandReply& Message, const IMessageContextRef& Context )
{
// Allow the next command to execute
bExecuteNextNetworkCommand = true;
// We should never be executing sub-commands of a network command when we're waiting for a cue for the next network command
check(bExecutingNetworkCommandResults == false);
}
void FAutomationWorkerModule::HandlePingMessage( const FAutomationWorkerPing& Message, const IMessageContextRef& Context )
{
MessageEndpoint->Send(new FAutomationWorkerPong(), Context->GetSender());
}
void FAutomationWorkerModule::HandleResetTests( const FAutomationWorkerResetTests& Message, const IMessageContextRef& Context )
{
FAutomationTestFramework::GetInstance().ResetTests();
}
void FAutomationWorkerModule::HandleRequestTestsMessage( const FAutomationWorkerRequestTests& Message, const IMessageContextRef& Context )
{
FAutomationTestFramework::GetInstance().LoadTestModules();
FAutomationTestFramework::GetInstance().SetDeveloperDirectoryIncluded(Message.DeveloperDirectoryIncluded);
FAutomationTestFramework::GetInstance().SetRequestedTestFilter(Message.RequestedTestFlags);
FAutomationTestFramework::GetInstance().GetValidTestNames( TestInfo );
SendTests(Context->GetSender());
}
void FAutomationWorkerModule::HandlePreTestingEvent()
{
#if WITH_ENGINE
if (!GIsEditor && GEngine->GameViewport)
{
GEngine->GameViewport->OnScreenshotCaptured().AddRaw(this, &FAutomationWorkerModule::HandleScreenShotCaptured);
}
//Register the editor screen shot callback
FAutomationTestFramework::GetInstance().OnScreenshotCaptured().BindRaw(this, &FAutomationWorkerModule::HandleScreenShotCapturedWithName);
#endif
}
void FAutomationWorkerModule::HandlePostTestingEvent()
{
#if WITH_ENGINE
if (!GIsEditor && GEngine->GameViewport)
{
GEngine->GameViewport->OnScreenshotCaptured().RemoveAll(this);
}
//Register the editor screen shot callback
FAutomationTestFramework::GetInstance().OnScreenshotCaptured().BindRaw(this, &FAutomationWorkerModule::HandleScreenShotCapturedWithName);
#endif
}
#if WITH_ENGINE
void FAutomationWorkerModule::HandleScreenShotCaptured(int32 Width, int32 Height, const TArray<FColor>& Bitmap)
{
FString Filename = FScreenshotRequest::GetFilename();
HandleScreenShotCapturedWithName(Width, Height, Bitmap, Filename);
}
void FAutomationWorkerModule::HandleScreenShotCapturedWithName(int32 Width, int32 Height, const TArray<FColor>& Bitmap, const FString& ScreenShotName)
{
if( FAutomationTestFramework::GetInstance().IsScreenshotAllowed() )
{
int32 NewHeight = Height;
int32 NewWidth = Width;
TArray<FColor> ScaledBitmap;
if( FAutomationTestFramework::GetInstance().ShouldUseFullSizeScreenshots() )
{
ScaledBitmap = Bitmap;
//Clear the alpha channel before saving
for ( int32 Index = 0; Index < Width*Height; Index++ )
{
ScaledBitmap[Index].A = 255;
}
}
else
{
//Set the thumbnail size
NewHeight = 128;
NewWidth = 256;
// Create and save the thumbnail
FImageUtils::CropAndScaleImage(Width, Height, NewWidth, NewHeight, Bitmap, ScaledBitmap);
}
TArray<uint8> CompressedBitmap;
FImageUtils::CompressImageArray(NewWidth, NewHeight, ScaledBitmap, CompressedBitmap);
// Send the screen shot if we have a target
if( TestRequesterAddress.IsValid() )
{
FAutomationWorkerScreenImage* Message = new FAutomationWorkerScreenImage();
FString SFilename = ScreenShotName;
Message->ScreenShotName = SFilename;
Message->ScreenImage = CompressedBitmap;
MessageEndpoint->Send(Message, TestRequesterAddress);
}
else
{
//Save locally
const bool bTree = true;
const FString FileName = FPaths::RootDir() + ScreenShotName;
IFileManager::Get().MakeDirectory( *FPaths::GetPath(FileName), bTree );
FFileHelper::SaveArrayToFile( CompressedBitmap, *FileName );
}
}
}
#endif
void FAutomationWorkerModule::HandleRunTestsMessage( const FAutomationWorkerRunTests& Message, const IMessageContextRef& Context )
{
ExecutionCount = Message.ExecutionCount;
TestName = Message.TestName;
BeautifiedTestName = Message.BeautifiedTestName;
bSendAnalytics = Message.bSendAnalytics;
TestRequesterAddress = Context->GetSender();
FAutomationTestFramework::GetInstance().SetScreenshotOptions(Message.bScreenshotsEnabled, Message.bUseFullSizeScreenShots);
// Always allow the first network command to execute
bExecuteNextNetworkCommand = true;
// We are not executing network command sub-commands right now
bExecutingNetworkCommandResults = false;
FAutomationTestFramework::GetInstance().StartTestByName(Message.TestName, Message.RoleIndex);
}
//dispatches analytics events to the data collector
void FAutomationWorkerModule::SendAnalyticsEvents(TArray<FString>& InAnalyticsItems)
{
for (int32 i = 0; i < InAnalyticsItems.Num(); ++i)
{
FString EventString = InAnalyticsItems[i];
if( EventString.EndsWith( TEXT( ",PERF" ) ) )
{
// Chop the ",PERF" off the end
EventString = EventString.Left( EventString.Len() - 5 );
FAutomationPerformanceSnapshot PerfSnapshot;
PerfSnapshot.FromCommaDelimitedString( EventString );
RecordPerformanceAnalytics( PerfSnapshot );
}
}
}
void FAutomationWorkerModule::RecordPerformanceAnalytics( const FAutomationPerformanceSnapshot& PerfSnapshot )
{
// @todo: Pass in additional performance capture data from incoming FAutomationPerformanceSnapshot!
FAutomationAnalytics::FireEvent_FPSCapture(PerfSnapshot);
}
#undef LOCTEXT_NAMESPACE