Files
UnrealEngineUWP/Engine/Source/Runtime/Launch/Private/Android/AndroidEventManager.cpp
Ben Marsh f402006cb7 Merging //UE4/Release-4.11 to //UE4/Main (Up to CL#2867947)
==========================
MAJOR FEATURES + CHANGES
==========================

Change 2858603 on 2016/02/08 by Tim.Hobson

	#jira UE-26550 - checked in new art assets for buttons and symbols

Change 2858665 on 2016/02/08 by Taizyd.Korambayil

	#jira UE-25797 Added TextureLODSettings for Ipad Mini set all LODBias to 2.

Change 2858668 on 2016/02/08 by Matthew.Griffin

	Added InfiltratorDemo back into Rocket samples

	#jira UEB-591

Change 2858743 on 2016/02/08 by Taizyd.Korambayil

	#jira UE-25996 Fixed Import Error in TopDOwn Code

Change 2858776 on 2016/02/08 by Matthew.Griffin

	Added UnrealMatch3 to packaged projects

	#jira UEB-589

Change 2858900 on 2016/02/08 by Taizyd.Korambayil

	#jira UE-15234 Switched all Mask Textures to use the (Mask,No sRGB) Compression

Change 2858947 on 2016/02/08 by Mike.Beach

	Controlling more when VerifyImport() is ran - trying to  prevent Verify() from running when DeferDependencyLoads is on, and instead trying to fully verify every import upfront (where it's meant to happen) before serializing in the package's contents (to alleviate cyclic dependency complications).

	#jira UE-21098

Change 2858954 on 2016/02/08 by Taizyd.Korambayil

	#jira UE-25524 Resaved Sound Assets to Fix NodeGuid Warnings

Change 2859126 on 2016/02/08 by Max.Chen

	Sequencer: Release track editors when destroying sequencer

	#jira UE-26423

Change 2859147 on 2016/02/08 by Martin.Wilson

	Fix uninitialized variable bug

	#jira UE-26606

Change 2859237 on 2016/02/08 by Lauren.Ridge

	Bumping Match 3 Version Number for iTunes Connect

	#jira UE-26648

Change 2859434 on 2016/02/08 by Chad.Taylor

	Handle the quit and focus message pipe from the SteamVR SDK

	#jira UEBP-142

Change 2859562 on 2016/02/08 by Chad.Taylor

	Mac/Android compile fix

	#jira UEBP-142

Change 2859633 on 2016/02/08 by Dan.Oconnor

	Transaction buffer uniformly address subobjects and SCS created components via an array of names and a root object. This allows undo/redo to work reliably to any depth of object hierarchy. Removed FReferencedObject and replaced it with the robust FPersistentObjectRef.

	DefaultSubObjects of the CDO are now tagged as RF_Archetype at construction (logic in PropertyHandleImpl.cpp probably no longer required)

	Actors reinstanced due to blueprint compilation now have stable names, so that this name can be used to reference their subobjects.

	This is also part of the fix needed for UE-23335, completely fixes UE-26045

	This version of the fix is less aggressive about searching all the way up an object's outer chain before stopping. Fixes issues with parts of outer chain changing on PIE. Also doesn't add objects referenced by subobject name to any AddReference calls which fixes race conditions with GC.

	Also fixes bad logic in CopyPropertiesForUnrelatedObjects, which would create copies of subobjects that already existed because we were populating the ReferenceReplacementMap before adding all existing subobjects (always components in this case)

	#jira UE-26045

Change 2859640 on 2016/02/08 by Dan.Oconnor

	Removed debugging code..

	#jira UE-26045

Change 2859668 on 2016/02/08 by Aaron.McLeran

	#jira UE-26503 A Mixer with a Concatenator node won't loop with a Looping node

	- issue was the looping nodes weren't properly reseting all the child wave instances
	- also looping nodes weren't reporting the correct GetNumSounds() count for use with sequencer node

Change 2859688 on 2016/02/08 by Chris.Babcock

	Allow external access to runtime modifications to OpenGL shaders
	#jira UE-26679
	#ue4

Change 2859739 on 2016/02/08 by Chad.Taylor

	UE4_Win64_Mono compile fix

	#jira UEBP-142

Change 2859962 on 2016/02/09 by Chris.Wood

	Passing command line to Crash Report Client without stripping the project name.
	[UE-24959] - "Send and Restart" brings up the Project Browser
	#jira UE-24959

	Reimplement changes from Orion in UE 4.11
	Reimplementing the command line logging filtering over from Dev-Core (same change as CL 2821359 that moved this change into Orion)
	Reimplementing passing full command line to Crash Report Client (same change as CL 2858617 in Orion)

Change 2859966 on 2016/02/09 by Matthew.Griffin

	Fixed shadow variable issue that was causing build failure in NonUnity mode on Mac

[CL 2873884 by Ben Marsh in Main branch]
2016-02-19 13:49:13 -05:00

456 lines
11 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "LaunchPrivatePCH.h"
#include "AndroidEventManager.h"
#include "AndroidApplication.h"
#include "AudioDevice.h"
#include "CallbackDevice.h"
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include "IHeadMountedDisplay.h"
DEFINE_LOG_CATEGORY(LogAndroidEvents);
FAppEventManager* FAppEventManager::sInstance = NULL;
FAppEventManager* FAppEventManager::GetInstance()
{
if(!sInstance)
{
sInstance = new FAppEventManager();
}
return sInstance;
}
void FAppEventManager::Tick()
{
bool bWindowCreatedThisTick = false;
while (!Queue.IsEmpty())
{
bool bDestroyWindow = false;
FAppEventData Event = DequeueAppEvent();
switch (Event.State)
{
case APP_EVENT_STATE_WINDOW_CREATED:
check(FirstInitialized);
bCreateWindow = true;
PendingWindow = (ANativeWindow*)Event.Data;
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("APP_EVENT_STATE_WINDOW_CREATED, %d, %d, %d"), int(bRunning), int(bHaveWindow), int(bHaveGame));
break;
case APP_EVENT_STATE_WINDOW_RESIZED:
case APP_EVENT_STATE_WINDOW_CHANGED:
// React on device orientation/windowSize changes only when application has window
// In case window was created this tick it should already has correct size
if (bHaveWindow && !bWindowCreatedThisTick)
{
ExecWindowResized();
}
break;
case APP_EVENT_STATE_SAVE_STATE:
bSaveState = true; //todo android: handle save state.
break;
case APP_EVENT_STATE_WINDOW_DESTROYED:
if (GEngine->HMDDevice.IsValid() && GEngine->HMDDevice->IsHMDConnected())
{
// delay the destruction until after the renderer teardown on GearVR
bDestroyWindow = true;
}
else
{
FAndroidAppEntry::DestroyWindow();
FPlatformMisc::SetHardwareWindow(NULL);
}
bHaveWindow = false;
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("APP_EVENT_STATE_WINDOW_DESTROYED, %d, %d, %d"), int(bRunning), int(bHaveWindow), int(bHaveGame));
break;
case APP_EVENT_STATE_ON_START:
//doing nothing here
break;
case APP_EVENT_STATE_ON_DESTROY:
if (FTaskGraphInterface::IsRunning())
{
FGraphEventRef WillTerminateTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
FCoreDelegates::ApplicationWillTerminateDelegate.Broadcast();
}, TStatId(), NULL, ENamedThreads::GameThread);
FTaskGraphInterface::Get().WaitUntilTaskCompletes(WillTerminateTask);
}
GIsRequestingExit = true; //destroy immediately. Game will shutdown.
FirstInitialized = false;
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("APP_EVENT_STATE_ON_DESTROY"));
break;
case APP_EVENT_STATE_ON_STOP:
bHaveGame = false;
break;
case APP_EVENT_STATE_ON_PAUSE:
bHaveGame = false;
break;
case APP_EVENT_STATE_ON_RESUME:
bHaveGame = true;
break;
// window focus events that follow their own hierarchy, and might or might not respect App main events hierarchy
case APP_EVENT_STATE_WINDOW_GAINED_FOCUS:
bWindowInFocus = true;
break;
case APP_EVENT_STATE_WINDOW_LOST_FOCUS:
bWindowInFocus = false;
break;
default:
UE_LOG(LogAndroidEvents, Display, TEXT("Application Event : %u not handled. "), Event.State);
}
if (bCreateWindow)
{
// wait until activity is in focus.
if (bWindowInFocus)
{
ExecWindowCreated();
bCreateWindow = false;
bHaveWindow = true;
bWindowCreatedThisTick = true;
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("ExecWindowCreated, %d, %d, %d"), int(bRunning), int(bHaveWindow), int(bHaveGame));
}
}
if (!bRunning && bHaveWindow && bHaveGame)
{
ResumeRendering();
ResumeAudio();
// broadcast events after the rendering thread has resumed
if (FTaskGraphInterface::IsRunning())
{
FGraphEventRef EnterForegroundTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
FCoreDelegates::ApplicationHasEnteredForegroundDelegate.Broadcast();
}, TStatId(), NULL, ENamedThreads::GameThread);
FGraphEventRef ReactivateTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
FCoreDelegates::ApplicationHasReactivatedDelegate.Broadcast();
}, TStatId(), EnterForegroundTask, ENamedThreads::GameThread);
FTaskGraphInterface::Get().WaitUntilTaskCompletes(ReactivateTask);
}
bRunning = true;
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Execution has been resumed!"));
}
else if (bRunning && (!bHaveWindow || !bHaveGame))
{
// broadcast events before rendering thread suspends
if (FTaskGraphInterface::IsRunning())
{
FGraphEventRef DeactivateTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
FCoreDelegates::ApplicationWillDeactivateDelegate.Broadcast();
}, TStatId(), NULL, ENamedThreads::GameThread);
FGraphEventRef EnterBackgroundTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
FCoreDelegates::ApplicationWillEnterBackgroundDelegate.Broadcast();
}, TStatId(), DeactivateTask, ENamedThreads::GameThread);
FTaskGraphInterface::Get().WaitUntilTaskCompletes(EnterBackgroundTask);
}
PauseRendering();
PauseAudio();
bRunning = false;
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Execution has been paused..."));
}
if (bDestroyWindow)
{
FAndroidAppEntry::DestroyWindow();
FPlatformMisc::SetHardwareWindow(NULL);
bDestroyWindow = false;
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("FAndroidAppEntry::DestroyWindow() called"));
}
}
if (EmptyQueueHandlerEvent)
{
EmptyQueueHandlerEvent->Trigger();
}
if (!bRunning && FirstInitialized)
{
EventHandlerEvent->Wait();
}
}
void FAppEventManager::TriggerEmptyQueue()
{
if (EmptyQueueHandlerEvent)
{
EmptyQueueHandlerEvent->Trigger();
}
}
FAppEventManager::FAppEventManager():
EventHandlerEvent(nullptr)
,EmptyQueueHandlerEvent(nullptr)
,FirstInitialized(false)
,bCreateWindow(false)
,bWindowInFocus(false)
,bSaveState(false)
,bAudioPaused(false)
,PendingWindow(NULL)
,bHaveWindow(false)
,bHaveGame(false)
,bRunning(false)
{
pthread_mutex_init(&MainMutex, NULL);
pthread_mutex_init(&QueueMutex, NULL);
}
void FAppEventManager::HandleWindowCreated(void* InWindow)
{
int rc = pthread_mutex_lock(&MainMutex);
check(rc == 0);
bool AlreadyInited = FirstInitialized;
rc = pthread_mutex_unlock(&MainMutex);
check(rc == 0);
// Make sure window will not be deleted until event is processed
// Window could be deleted by OS while event queue stuck at game start-up phase
FAndroidWindow::AcquireWindowRef((ANativeWindow*)InWindow);
if(AlreadyInited)
{
EnqueueAppEvent(APP_EVENT_STATE_WINDOW_CREATED, InWindow );
}
else
{
//This cannot wait until first tick.
rc = pthread_mutex_lock(&MainMutex);
check(rc == 0);
check(FPlatformMisc::GetHardwareWindow() == NULL);
FPlatformMisc::SetHardwareWindow(InWindow);
FirstInitialized = true;
rc = pthread_mutex_unlock(&MainMutex);
check(rc == 0);
EnqueueAppEvent(APP_EVENT_STATE_WINDOW_CREATED, InWindow );
}
}
void FAppEventManager::SetEventHandlerEvent(FEvent* InEventHandlerEvent)
{
EventHandlerEvent = InEventHandlerEvent;
}
void FAppEventManager::SetEmptyQueueHandlerEvent(FEvent* InEventHandlerEvent)
{
EmptyQueueHandlerEvent = InEventHandlerEvent;
}
void FAppEventManager::PauseRendering()
{
if(GUseThreadedRendering )
{
if (GIsThreadedRendering)
{
StopRenderingThread();
}
}
else
{
RHIReleaseThreadOwnership();
}
}
void FAppEventManager::ResumeRendering()
{
if( GUseThreadedRendering )
{
if (!GIsThreadedRendering)
{
StartRenderingThread();
}
}
else
{
RHIAcquireThreadOwnership();
}
}
void FAppEventManager::ExecWindowCreated()
{
UE_LOG(LogAndroidEvents, Display, TEXT("ExecWindowCreated"));
check(PendingWindow)
FPlatformMisc::SetHardwareWindow(PendingWindow);
// When application launched while device is in sleep mode SystemResolution could be set to opposite orientation values
// Force to update SystemResolution to current values whenever we create a new window
FPlatformRect ScreenRect = FAndroidWindow::GetScreenRect();
FSystemResolution::RequestResolutionChange(ScreenRect.Right, ScreenRect.Bottom, EWindowMode::Fullscreen);
FAndroidAppEntry::ReInitWindow();
// We hold this reference to ensure that window will not be deleted while game starting up
// release it when window is finally initialized
FAndroidWindow::ReleaseWindowRef(PendingWindow);
PendingWindow = nullptr;
FAndroidApplication::OnWindowSizeChanged();
}
void FAppEventManager::ExecWindowResized()
{
if (bRunning)
{
FlushRenderingCommands();
}
FAndroidWindow::InvalidateCachedScreenRect();
FAndroidAppEntry::ReInitWindow();
FAndroidApplication::OnWindowSizeChanged();
}
void FAppEventManager::PauseAudio()
{
bAudioPaused = true;
if (GEngine->GetMainAudioDevice())
{
GEngine->GetMainAudioDevice()->Suspend(false);
}
}
void FAppEventManager::ResumeAudio()
{
bAudioPaused = false;
if (GEngine->GetMainAudioDevice())
{
GEngine->GetMainAudioDevice()->Suspend(true);
}
}
void FAppEventManager::EnqueueAppEvent(EAppEventState InState, void* InData)
{
FAppEventData Event;
Event.State = InState;
Event.Data = InData;
int rc = pthread_mutex_lock(&QueueMutex);
check(rc == 0);
Queue.Enqueue(Event);
if (EmptyQueueHandlerEvent)
{
EmptyQueueHandlerEvent->Reset();
}
rc = pthread_mutex_unlock(&QueueMutex);
check(rc == 0);
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("LogAndroidEvents: EnqueueAppEvent : %u, %u, tid = %d"), InState, (uintptr_t)InData, gettid());
}
FAppEventData FAppEventManager::DequeueAppEvent()
{
int rc = pthread_mutex_lock(&QueueMutex);
check(rc == 0);
FAppEventData OutData;
Queue.Dequeue( OutData );
rc = pthread_mutex_unlock(&QueueMutex);
check(rc == 0);
UE_LOG(LogAndroidEvents, Display, TEXT("DequeueAppEvent : %u, %u"), OutData.State, (uintptr_t)OutData.Data)
return OutData;
}
bool FAppEventManager::IsGamePaused()
{
return !bRunning;
}
bool FAppEventManager::WaitForEventInQueue(EAppEventState InState, double TimeoutSeconds)
{
bool FoundEvent = false;
double StopTime = FPlatformTime::Seconds() + TimeoutSeconds;
TQueue<FAppEventData, EQueueMode::Spsc> HoldingQueue;
while (!FoundEvent)
{
int rc = pthread_mutex_lock(&QueueMutex);
check(rc == 0);
// Copy the existing queue (and check for our event)
while (!Queue.IsEmpty())
{
FAppEventData OutData;
Queue.Dequeue(OutData);
if (OutData.State == InState)
FoundEvent = true;
HoldingQueue.Enqueue(OutData);
}
if (FoundEvent)
break;
// Time expired?
if (FPlatformTime::Seconds() > StopTime)
break;
// Unlock for new events and wait a bit before trying again
rc = pthread_mutex_unlock(&QueueMutex);
check(rc == 0);
FPlatformProcess::Sleep(0.01f);
}
// Add events back to queue from holding
while (!HoldingQueue.IsEmpty())
{
FAppEventData OutData;
HoldingQueue.Dequeue(OutData);
Queue.Enqueue(OutData);
}
int rc = pthread_mutex_unlock(&QueueMutex);
check(rc == 0);
return FoundEvent;
}
extern volatile bool GEventHandlerInitialized;
void FAppEventManager::WaitForEmptyQueue()
{
if (EmptyQueueHandlerEvent && GEventHandlerInitialized && !GIsRequestingExit)
{
EmptyQueueHandlerEvent->Wait();
}
}