VRTowerDef/Plugins/MetaXR/Source/OculusXRHMD/Private/OculusXRHMD_StressTester.cpp

310 lines
9.7 KiB
C++

// @lint-ignore-every LICENSELINT
// Copyright Epic Games, Inc. All Rights Reserved.
#include "OculusXRHMD_StressTester.h"
#if OCULUS_STRESS_TESTS_ENABLED
#include "OculusXRHMD.h"
#include "GlobalShader.h"
#include "UniformBuffer.h"
#include "RHICommandList.h"
#include "ShaderParameterUtils.h"
#include "RHIStaticStates.h"
#include "PipelineStateCache.h"
#include "OculusShaders.h"
#include "SceneUtils.h" // for SCOPED_DRAW_EVENT()
DECLARE_STATS_GROUP(TEXT("Oculus"), STATGROUP_Oculus, STATCAT_Advanced);
DECLARE_CYCLE_STAT(TEXT("GPUStressRendering"), STAT_GPUStressRendering, STATGROUP_Oculus);
//-------------------------------------------------------------------------------------------------
// Uniform buffers
//-------------------------------------------------------------------------------------------------
//This buffer should contain variables that never, or rarely change
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderConstantParameters, )
//SHADER_PARAMETER(FVector4, Name)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderConstantParameters, "PSConstants");
typedef TUniformBufferRef<FOculusPixelShaderConstantParameters> FOculusPixelShaderConstantParametersRef;
//This buffer is for variables that change very often (each frame for example)
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderVariableParameters, )
SHADER_PARAMETER(int, IterationsMultiplier)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FOculusPixelShaderVariableParameters, "PSVariables");
typedef TUniformBufferRef<FOculusPixelShaderVariableParameters> FOculusPixelShaderVariableParametersRef;
namespace OculusXRHMD
{
//-------------------------------------------------------------------------------------------------
// FTextureVertexDeclaration
//-------------------------------------------------------------------------------------------------
struct FTextureVertex
{
FVector4 Position;
FVector2f UV;
};
class FTextureVertexDeclaration : public FRenderResource
{
public:
FVertexDeclarationRHIRef VertexDeclarationRHI;
#if UE_VERSION_OLDER_THAN(5, 3, 0)
virtual void InitRHI() override
#else
virtual void InitRHI(FRHICommandListBase& RHICmdList) override
#endif
{
FVertexDeclarationElementList Elements;
uint32 Stride = sizeof(FTextureVertex);
Elements.Add(FVertexElement(0, STRUCT_OFFSET(FTextureVertex, Position), VET_Float4, 0, Stride));
Elements.Add(FVertexElement(0, STRUCT_OFFSET(FTextureVertex, UV), VET_Float2, 1, Stride));
VertexDeclarationRHI = PipelineStateCache::GetOrCreateVertexDeclaration(Elements);
}
virtual void ReleaseRHI() override
{
VertexDeclarationRHI.SafeRelease();
}
};
static TGlobalResource<FTextureVertexDeclaration> GOculusTextureVertexDeclaration;
//-------------------------------------------------------------------------------------------------
// FStressTester
//-------------------------------------------------------------------------------------------------
TSharedPtr<FStressTester, ESPMode::ThreadSafe> FStressTester::SharedInstance;
TSharedRef<class FStressTester, ESPMode::ThreadSafe> FStressTester::Get()
{
CheckInGameThread();
if (!SharedInstance.IsValid())
{
SharedInstance = TSharedPtr<class FStressTester, ESPMode::ThreadSafe>(new FStressTester());
check(SharedInstance.IsValid());
}
return SharedInstance.ToSharedRef();
}
FStressTester::FStressTester()
: Mode(STM_None)
, CPUSpinOffInSeconds(0.011 / 3.) // one third of the frame (default value)
, PDsTimeLimitInSeconds(10.) // 10 secs
, CPUsTimeLimitInSeconds(10.) // 10 secs
, GPUsTimeLimitInSeconds(10.) // 10 secs
, GPUIterationsMultiplier(0.)
, CPUStartTimeInSeconds(0.)
, GPUStartTimeInSeconds(0.)
, PDStartTimeInSeconds(0.)
{
}
// multiple masks could be set, see EStressTestMode
void FStressTester::SetStressMode(uint32 InStressMask)
{
check((InStressMask & (~STM__All)) == 0);
Mode = InStressMask;
for (uint32 m = 1; m < STM__All; m <<= 1)
{
if (InStressMask & m)
{
switch (m)
{
case STM_EyeBufferRealloc:
UE_LOG(LogHMD, Log, TEXT("PD of EyeBuffer stress test is started"));
break;
case STM_CPUSpin:
UE_LOG(LogHMD, Log, TEXT("CPU stress test is started"));
break;
case STM_GPU:
UE_LOG(LogHMD, Log, TEXT("GPU stress test is started"));
break;
}
}
}
}
void FStressTester::DoTickCPU_GameThread(FOculusXRHMD* pPlugin)
{
CheckInGameThread();
if (Mode & STM_EyeBufferRealloc)
{
// Change PixelDensity every frame within MinPixelDensity..MaxPixelDensity range
if (PDStartTimeInSeconds == 0.)
{
PDStartTimeInSeconds = FPlatformTime::Seconds();
}
else
{
const double Now = FPlatformTime::Seconds();
if (Now - PDStartTimeInSeconds >= PDsTimeLimitInSeconds)
{
PDStartTimeInSeconds = 0.;
Mode &= ~STM_EyeBufferRealloc;
UE_LOG(LogHMD, Log, TEXT("PD of EyeBuffer stress test is finished"));
}
}
const int divisor = int((MaxPixelDensity - MinPixelDensity) * 10.f);
float NewPD = float(uint64(FPlatformTime::Seconds() * 1000) % divisor) / 10.f + MinPixelDensity;
pPlugin->SetPixelDensity(NewPD);
}
if (Mode & STM_CPUSpin)
{
// Simulate heavy CPU load within specified time limits
if (CPUStartTimeInSeconds == 0.)
{
CPUStartTimeInSeconds = FPlatformTime::Seconds();
}
else
{
const double Now = FPlatformTime::Seconds();
if (Now - CPUStartTimeInSeconds >= CPUsTimeLimitInSeconds)
{
CPUStartTimeInSeconds = 0.;
Mode &= ~STM_CPUSpin;
UE_LOG(LogHMD, Log, TEXT("CPU stress test is finished"));
}
}
const double StartSeconds = FPlatformTime::Seconds();
int i, num = 1, primes = 0;
bool bFinish = false;
while (!bFinish)
{
i = 2;
while (i <= num)
{
if (num % i == 0)
{
break;
}
i++;
const double NowSeconds = FPlatformTime::Seconds();
if (NowSeconds - StartSeconds >= CPUSpinOffInSeconds)
{
bFinish = true;
}
}
if (i == num)
{
++primes;
}
++num;
}
}
if (Mode & STM_GPU)
{
// Simulate heavy CPU load within specified time limits
if (GPUStartTimeInSeconds == 0.)
{
GPUStartTimeInSeconds = FPlatformTime::Seconds();
}
else
{
const double Now = FPlatformTime::Seconds();
if (Now - GPUStartTimeInSeconds >= GPUsTimeLimitInSeconds)
{
GPUStartTimeInSeconds = 0.;
Mode &= ~STM_GPU;
UE_LOG(LogHMD, Log, TEXT("GPU stress test is finished"));
}
}
}
}
//-------------------------------------------------------------------------------------------------
// Console commands for managing the stress tester:
//-------------------------------------------------------------------------------------------------
static void StressGPUCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(FStressTester::STM_GPU | StressTester->GetStressMode());
if (Args.Num() > 0)
{
const int GpuMult = FCString::Atoi(*Args[0]);
StressTester->SetGPULoadMultiplier(GpuMult);
}
if (Args.Num() > 1)
{
const float GpuTimeLimit = FCString::Atof(*Args[1]);
StressTester->SetGPUsTimeLimitInSeconds(GpuTimeLimit);
}
}
static FAutoConsoleCommand CStressGPUCmd(
TEXT("vr.oculus.Stress.GPU"),
*NSLOCTEXT("OculusRift", "CCommandText_StressGPU", "Initiates a GPU stress test.\n Usage: vr.oculus.Stress.GPU [LoadMultiplier [TimeLimit]]").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressGPUCmdHandler));
static void StressCPUCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(FStressTester::STM_CPUSpin | StressTester->GetStressMode());
if (Args.Num() > 0)
{
const float CpuLimit = FCString::Atof(*Args[0]);
StressTester->SetCPUSpinOffPerFrameInSeconds(CpuLimit);
}
if (Args.Num() > 1)
{
const float CpuTimeLimit = FCString::Atof(*Args[1]);
StressTester->SetCPUsTimeLimitInSeconds(CpuTimeLimit);
}
}
static FAutoConsoleCommand CStressCPUCmd(
TEXT("vr.oculus.Stress.CPU"),
*NSLOCTEXT("OculusRift", "CCommandText_StressCPU", "Initiates a CPU stress test.\n Usage: vr.oculus.Stress.CPU [PerFrameTime [TotalTimeLimit]]").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressCPUCmdHandler));
static void StressPDCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(FStressTester::STM_EyeBufferRealloc | StressTester->GetStressMode());
if (Args.Num() > 0)
{
const float TimeLimit = FCString::Atof(*Args[0]);
StressTester->SetPDsTimeLimitInSeconds(TimeLimit);
}
}
static FAutoConsoleCommand CStressPDCmd(
TEXT("vr.oculus.Stress.PD"),
*NSLOCTEXT("OculusRift", "CCommandText_StressPD", "Initiates a pixel density stress test wher pixel density is changed every frame for TotalTimeLimit seconds.\n Usage: vr.oculus.Stress.PD [TotalTimeLimit]").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressPDCmdHandler));
static void StressResetCmdHandler(const TArray<FString>& Args, UWorld* World, FOutputDevice& Ar)
{
auto StressTester = FStressTester::Get();
StressTester->SetStressMode(0);
}
static FAutoConsoleCommand CStressResetCmd(
TEXT("vr.oculus.Stress.Reset"),
*NSLOCTEXT("OculusRift", "CCommandText_StressReset", "Resets the stress tester and stops all currently running stress tests.\n Usage: vr.oculus.Stress.Reset").ToString(),
FConsoleCommandWithWorldArgsAndOutputDeviceDelegate::CreateStatic(StressResetCmdHandler));
} // namespace OculusXRHMD
#endif // #if OCULUS_STRESS_TESTS_ENABLED