338 lines
12 KiB
C++
338 lines
12 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (c) Electronic Arts Inc. All rights reserved.
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "EABase/eabase.h"
|
|
#if (defined(EA_PLATFORM_MICROSOFT) && !defined(CS_UNDEFINED_STRING) && !EA_WINAPI_FAMILY_PARTITION(EA_WINAPI_PARTITION_DESKTOP))
|
|
#include <EAAssert/eaassert.h>
|
|
#include <EAMain/EAMain.h>
|
|
#include <EAStdC/EASprintf.h>
|
|
#include <EAStdC/EAString.h>
|
|
|
|
EA_DISABLE_ALL_VC_WARNINGS()
|
|
#pragma warning(disable: 4472 4355) // additional warnings generated by XDK with VS2015
|
|
#include <memory>
|
|
#ifndef WIN32_LEAN_AND_MEAN
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
#include <Windows.h>
|
|
|
|
#if defined EA_PLATFORM_CAPILANO
|
|
#if defined EAMAIN_CAPILANO_DX12
|
|
#include <d3d12_x.h>
|
|
#else
|
|
#include <d3d11_x.h>
|
|
#endif
|
|
#endif
|
|
EA_RESTORE_ALL_VC_WARNINGS()
|
|
|
|
namespace EA
|
|
{
|
|
namespace EAMain
|
|
{
|
|
|
|
// Application - implements the required functionality for a application
|
|
ref class ApplicationView sealed : public Windows::ApplicationModel::Core::IFrameworkView
|
|
{
|
|
public:
|
|
// IFrameworkView Methods
|
|
virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView);
|
|
virtual void SetWindow(Windows::UI::Core::CoreWindow^ window) {}
|
|
virtual void Load(Platform::String^ entryPoint);
|
|
virtual void Run();
|
|
virtual void Uninitialize();
|
|
private:
|
|
char *mCommandLine;
|
|
#if EA_PLATFORM_CAPILANO
|
|
#if defined EAMAIN_CAPILANO_DX12
|
|
ID3D12CommandQueue* mpCommandQueue;
|
|
#else
|
|
ID3DXboxPerformanceContext* mpD3DXboxPerfContext;
|
|
#endif
|
|
#endif
|
|
|
|
void CreateDeviceResources();
|
|
void OnActivated( Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args );
|
|
};
|
|
|
|
// ApplicationSource - responsible for creating the Application instance and passing it back to the system
|
|
ref class ApplicationViewSource : Windows::ApplicationModel::Core::IFrameworkViewSource
|
|
{
|
|
public:
|
|
virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView()
|
|
{
|
|
return ref new ApplicationView();
|
|
}
|
|
};
|
|
|
|
void OutputAppTransition(const char* transition, const wchar_t* sender, const wchar_t* args)
|
|
{
|
|
char msg[1024];
|
|
|
|
EA::StdC::Snprintf(msg, EAArrayCount(msg), "****\n app is %s.\n sender: ", transition);
|
|
EA::StdC::Strlcat(msg, sender, EAArrayCount(msg));
|
|
EA::StdC::Strlcat(msg, "\n args: ", EAArrayCount(msg));
|
|
EA::StdC::Strlcat(msg, args, EAArrayCount(msg));
|
|
EA::StdC::Strlcat(msg, "\n****\n", EAArrayCount(msg));
|
|
|
|
OutputDebugStringA(msg);
|
|
}
|
|
|
|
void ApplicationView::CreateDeviceResources()
|
|
{
|
|
#if defined EA_PLATFORM_CAPILANO
|
|
#if defined EAMAIN_CAPILANO_DX12
|
|
ID3D12Device* pD3DDevice;
|
|
|
|
HRESULT hr = D3D12CreateDevice(
|
|
nullptr, // specify null to use the default adapter
|
|
D3D_FEATURE_LEVEL_11_0,
|
|
__uuidof(ID3D12Device),
|
|
(void**)&pD3DDevice
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugStringA("Failed to create device. Suspending will not work for this application.");
|
|
return;
|
|
}
|
|
|
|
D3D12XBOX_COMMAND_QUEUE_DESC queueDesc = {};
|
|
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
|
|
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
|
queueDesc.EngineOrPipeIndex = 0;
|
|
queueDesc.QueueIndex = 0;
|
|
|
|
hr = pD3DDevice->CreateCommandQueueX(&queueDesc, __uuidof(ID3D12CommandQueue), (void**)&mpCommandQueue);
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugStringA("Failed to create command queue. Suspending will not work for this application.");
|
|
}
|
|
|
|
if (pD3DDevice) { pD3DDevice->Release(); pD3DDevice = NULL; }
|
|
#else
|
|
// This flag adds support for surfaces with a different color channel ordering than the API default.
|
|
// It is recommended usage, and is required for compatibility with Direct2D.
|
|
UINT creationFlags = D3D11_CREATE_DEVICE_INSTRUMENTED;
|
|
|
|
// This array defines the set of DirectX hardware feature levels this app will support.
|
|
// Note the ordering should be preserved.
|
|
// Don't forget to declare your application's minimum required feature level in its
|
|
// description. All applications are assumed to support 9.1 unless otherwise stated.
|
|
D3D_FEATURE_LEVEL featureLevels[] =
|
|
{
|
|
D3D_FEATURE_LEVEL_11_0
|
|
};
|
|
|
|
// Create the DX11 API device object, and get a corresponding context.
|
|
ID3D11Device* pD3DDevice;
|
|
ID3D11DeviceContext* pD3DDeviceContext;
|
|
|
|
HRESULT hr = D3D11CreateDevice(
|
|
nullptr, // specify null to use the default adapter
|
|
D3D_DRIVER_TYPE_HARDWARE,
|
|
nullptr, // leave as nullptr unless software device
|
|
creationFlags, // optionally set debug and Direct2D compatibility flags
|
|
featureLevels, // list of feature levels this app can support
|
|
ARRAYSIZE(featureLevels), // number of entries in above list
|
|
D3D11_SDK_VERSION, // always set this to D3D11_SDK_VERSION
|
|
&pD3DDevice, // returns the Direct3D device created
|
|
NULL, // returns feature level of device created
|
|
&pD3DDeviceContext // returns the device immediate context
|
|
);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugStringA("Failed to create device. Suspending will not work for this application.");
|
|
return;
|
|
}
|
|
|
|
hr = pD3DDevice->QueryInterface(__uuidof(mpD3DXboxPerfContext), (void **)&mpD3DXboxPerfContext);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
OutputDebugStringA("Failed to get perfcontext. Suspending will not work for this application.");
|
|
}
|
|
|
|
if (pD3DDevice) { pD3DDevice->Release(); pD3DDevice = NULL; }
|
|
if (pD3DDeviceContext) { pD3DDeviceContext->Release(); pD3DDeviceContext = NULL; }
|
|
#endif
|
|
#else
|
|
// Do nothing
|
|
#endif
|
|
}
|
|
|
|
// Called by the system. Perform application initialization here,
|
|
// hooking application wide events, etc.
|
|
void ApplicationView::Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView)
|
|
{
|
|
using namespace Platform;
|
|
using namespace Windows::Foundation;
|
|
using namespace Windows::ApplicationModel;
|
|
using namespace Windows::ApplicationModel::Core;
|
|
using namespace Windows::ApplicationModel::Activation;
|
|
|
|
// Creates any resources required by the platform to run an application / enable a particular feature set (like GPU profiling or suspend/resume)
|
|
CreateDeviceResources();
|
|
|
|
#pragma warning(push)
|
|
// Disables warning for MS class 'Windows::Foundation::TypedEventHandler<TSender,TResult>::{ctor}::__abi_PointerToMemberCapture'
|
|
// "layout of class may have changed from a previous version of the compiler due to better packing of member 'Windows::Foundation::TypedEventHandler<TSender,TResult>::{ctor}::__abi_PointerToMemberCapture::member'"
|
|
#pragma warning(disable:4371)
|
|
applicationView->Activated += ref new Windows::Foundation::TypedEventHandler< CoreApplicationView^, IActivatedEventArgs^ >( this, &ApplicationView::OnActivated );
|
|
#pragma warning(pop)
|
|
CoreApplication::Suspending += ref new EventHandler<SuspendingEventArgs^>([this](Object^ sender, SuspendingEventArgs^ args) {
|
|
OutputAppTransition("suspending", sender ? sender->ToString()->Data() : L"NULL", args ? args->ToString()->Data() : L"NULL");
|
|
#if defined EA_PLATFORM_CAPILANO
|
|
#if defined EAMAIN_CAPILANO_DX12
|
|
mpCommandQueue->SuspendX(0);
|
|
#else
|
|
mpD3DXboxPerfContext->Suspend(0);
|
|
#endif
|
|
#endif
|
|
});
|
|
CoreApplication::Resuming += ref new EventHandler<Object^>([this](Object^ sender, Object^ args) {
|
|
OutputAppTransition("resuming", sender ? sender->ToString()->Data() : L"NULL", args ? args->ToString()->Data() : L"NULL");
|
|
#if defined EA_PLATFORM_CAPILANO
|
|
#if defined EAMAIN_CAPILANO_DX12
|
|
mpCommandQueue->ResumeX();
|
|
#else
|
|
mpD3DXboxPerfContext->Resume();
|
|
#endif
|
|
#endif
|
|
});
|
|
CoreApplication::Exiting += ref new EventHandler<Object^>([](Object^ sender, Object^ args) {
|
|
OutputAppTransition("exiting", sender ? sender->ToString()->Data() : L"NULL", args ? args->ToString()->Data() : L"NULL");
|
|
});
|
|
}
|
|
|
|
static char *ConvertLaunchArgsToMultibyte(LPCWSTR rawArgumentString, int rawArgumentStringLength)
|
|
{
|
|
int bufferSize = WideCharToMultiByte(
|
|
CP_UTF8,
|
|
0,
|
|
rawArgumentString,
|
|
rawArgumentStringLength,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
|
|
char *commandLine = static_cast<char *>(calloc(bufferSize + 1, 1));
|
|
int rv = WideCharToMultiByte(
|
|
CP_UTF8,
|
|
0,
|
|
rawArgumentString,
|
|
rawArgumentStringLength,
|
|
commandLine,
|
|
bufferSize + 1,
|
|
NULL,
|
|
NULL);
|
|
|
|
commandLine[bufferSize] = 0;
|
|
EA_ASSERT(rv == bufferSize);
|
|
EA_UNUSED(rv); // avoids warnings in opt builds regarding unused variables
|
|
|
|
return commandLine;
|
|
}
|
|
|
|
static char *ReadArgsFromFile()
|
|
{
|
|
FILE *fp = fopen("EAMainArgsFile.txt", "rb");
|
|
|
|
if (fp == NULL)
|
|
{
|
|
goto error_return;
|
|
}
|
|
|
|
size_t fileSize;
|
|
fseek(fp, 0, SEEK_END);
|
|
fileSize = static_cast<size_t>(ftell(fp));
|
|
fseek(fp, 0, SEEK_SET);
|
|
|
|
char *argsBuffer = static_cast<char *>(calloc(fileSize + 1, 1));
|
|
|
|
if (fread(argsBuffer, 1, fileSize, fp) != fileSize)
|
|
{
|
|
goto error_return_free_buffer;
|
|
}
|
|
|
|
return argsBuffer;
|
|
|
|
error_return_free_buffer:
|
|
free(argsBuffer);
|
|
fclose(fp);
|
|
|
|
error_return:
|
|
return static_cast<char *>(calloc(1, 1));
|
|
}
|
|
|
|
void ApplicationView::OnActivated( Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args )
|
|
{
|
|
if (args->Kind == Windows::ApplicationModel::Activation::ActivationKind::Launch)
|
|
{
|
|
Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^launchArgs = (Windows::ApplicationModel::Activation::LaunchActivatedEventArgs ^) args;
|
|
Platform::String ^argumentString = launchArgs->Arguments;
|
|
LPCWSTR rawArgumentString = argumentString->Data();
|
|
int rawArgumentStringLength = argumentString->Length();
|
|
|
|
if (rawArgumentString == NULL || wcslen(rawArgumentString) == 0)
|
|
{
|
|
mCommandLine = ReadArgsFromFile();
|
|
}
|
|
else
|
|
{
|
|
mCommandLine = ConvertLaunchArgsToMultibyte(rawArgumentString, rawArgumentStringLength);
|
|
}
|
|
}
|
|
Windows::UI::Core::CoreWindow::GetForCurrentThread()->Activate();
|
|
}
|
|
|
|
void ApplicationView::Load(Platform::String^ entryPoint)
|
|
{
|
|
|
|
}
|
|
|
|
// Called by the system after initialization is complete. This implements the traditional game loop.
|
|
void ApplicationView::Run()
|
|
{
|
|
using namespace EA::EAMain;
|
|
std::unique_ptr<IWinRTRunner> winRTRunner(CreateWinRTRunner());
|
|
|
|
CommandLine commandline(mCommandLine, CommandLine::FLAG_NO_PROGRAM_NAME);
|
|
|
|
winRTRunner->Run(commandline.Argc(),commandline.Argv());
|
|
do
|
|
{
|
|
// ProcessEvents will throw if the process is exiting, allowing us to
|
|
// break out of the loop. This will be cleaned up when we get proper
|
|
// process lifetime management online in a future release.
|
|
Windows::UI::Core::CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent);
|
|
} while(!winRTRunner->IsFinished());
|
|
|
|
winRTRunner->ReportResult();
|
|
Windows::ApplicationModel::Core::CoreApplication::Exit();
|
|
|
|
free(mCommandLine);
|
|
mCommandLine = nullptr;
|
|
}
|
|
|
|
|
|
void ApplicationView::Uninitialize()
|
|
{
|
|
|
|
}
|
|
|
|
namespace Internal
|
|
{
|
|
EAMAIN_API void StartWinRtApplication()
|
|
{
|
|
// To do: store args so they can be passed to EAEntryPointMain.
|
|
Windows::ApplicationModel::Core::CoreApplication::Run(ref new ApplicationViewSource());
|
|
}
|
|
} // namespace Internal
|
|
} // namespace EAMain
|
|
} // namespace EA
|
|
|
|
#endif
|