Refactor GDK platform to be base for Xbox consoles

This commit is contained in:
Wojtek Figat
2021-08-26 17:28:16 +02:00
parent d9410f9cdd
commit 05fc1b8dd1
14 changed files with 1649 additions and 85 deletions

View File

@@ -117,9 +117,6 @@ bool GraphicsService::Init()
// Platform default
if (!device)
{
#if PLATFORM_WINDOWS
// Windows
#if GRAPHICS_API_DIRECTX11
if (!device)
device = CreateGPUDeviceDX11();
@@ -132,65 +129,9 @@ bool GraphicsService::Init()
if (!device)
device = CreateGPUDeviceVulkan();
#endif
#elif PLATFORM_UWP
// UWP
if (!device)
device = CreateGPUDeviceDX11();
#elif PLATFORM_LINUX
// Linux
#if GRAPHICS_API_VULKAN
if (!device)
device = CreateGPUDeviceVulkan();
#endif
#elif PLATFORM_PS4
// PS4
#if GRAPHICS_API_PS4
if (!device)
device = CreateGPUDevicePS4();
#endif
#elif PLATFORM_XBOX_ONE
// Xbox One
#if GRAPHICS_API_DIRECTX12
if (!device)
device = CreateGPUDeviceDX12();
#endif
#elif PLATFORM_XBOX_SCARLETT
// Xbox Scarlett
#if GRAPHICS_API_DIRECTX12
if (!device)
device = CreateGPUDeviceDX12();
#endif
#elif PLATFORM_ANDROID
// Android
#if GRAPHICS_API_VULKAN
if (!device)
device = CreateGPUDeviceVulkan();
#endif
#elif PLATFORM_SWITCH
// Switch
#if GRAPHICS_API_VULKAN
if (!device)
device = CreateGPUDeviceVulkan();
#endif
#elif !defined(GRAPHICS_API_NULL)
#error "Platform does not support GPU devices."
#endif
}

View File

@@ -198,20 +198,6 @@ GPUDeviceDX12::GPUDeviceDX12(IDXGIFactory4* dxgiFactory, GPUAdapterDX* adapter)
{
}
#if PLATFORM_XBOX_SCARLETT
namespace XboxScarlett
{
extern Action OnSuspend;
extern Action OnResume;
}
#elif PLATFORM_XBOX_ONE
namespace XboxOne
{
extern Action OnSuspend;
extern Action OnResume;
}
#endif
bool GPUDeviceDX12::Init()
{
#if PLATFORM_XBOX_SCARLETT || PLATFORM_XBOX_ONE
@@ -264,12 +250,9 @@ bool GPUDeviceDX12::Init()
LOG(Info, "Hardware Version: {0}", hwVer);
updateFrameEvents();
#if PLATFORM_XBOX_SCARLETT
XboxScarlett::OnSuspend.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspend>(this);
XboxScarlett::OnResume.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnResume>(this);
#elif PLATFORM_XBOX_ONE
XboxOne::OnSuspend.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspend>(this);
XboxOne::OnResume.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnResume>(this);
#if PLATFORM_GDK
GDKPlatform::OnSuspend.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnSuspend>(this);
GDKPlatform::OnResume.Bind<GPUDeviceDX12, &GPUDeviceDX12::OnResume>(this);
#endif
#else
// Get DXGI adapter

View File

@@ -0,0 +1,253 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "GDKInput.h"
#include "Engine/Input/Input.h"
#include "Engine/Input/Gamepad.h"
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
#include <GameInput.h>
static_assert(sizeof(Guid) <= sizeof(APP_LOCAL_DEVICE_ID), "Invalid Game Input deviceId size.");
String ToString(GameInputString const* name)
{
return name && name->data ? String(name->data) : String::Empty;
}
/// <summary>
/// Implementation of the gamepad device for GDK platform.
/// </summary>
/// <seealso cref="Gamepad" />
class GDKGamepad : public Gamepad
{
public:
/// <summary>
/// Initializes a new instance of the <see cref="GDKGamepad"/> class.
/// </summary>
/// <param name="device">The input device.</param>
explicit GDKGamepad(IGameInputDevice* device)
: Gamepad(*(Guid*)&device->GetDeviceInfo()->deviceId, ::ToString(device->GetDeviceInfo()->displayName))
, Device(device)
{
Device->AddRef();
}
/// <summary>
/// Finalizes an instance of the <see cref="GDKGamepad"/> class.
/// </summary>
~GDKGamepad()
{
Device->Release();
}
/// <summary>
/// The device.
/// </summary>
IGameInputDevice* Device;
public:
// [Gamepad]
void SetVibration(const GamepadVibrationState& state) override;
protected:
// [Gamepad]
bool UpdateState() override;
};
namespace GDKInputImpl
{
IGameInput* GameInput = nullptr;
IGameInputReading* PrevReading = nullptr;
}
using namespace GDKInputImpl;
bool IsSameDevice(const APP_LOCAL_DEVICE_ID& first, const APP_LOCAL_DEVICE_ID& second)
{
return memcmp(&first, &second, APP_LOCAL_DEVICE_ID_SIZE) == 0;
}
bool IsSameDevice(IGameInputDevice* first, IGameInputDevice* second)
{
return IsSameDevice(first->GetDeviceInfo()->deviceId, second->GetDeviceInfo()->deviceId);
}
void TryAddGamepad(IGameInputDevice* device)
{
for (auto& gamepad : Input::Gamepads)
{
if (IsSameDevice(((GDKGamepad*)gamepad)->Device, device))
return;
}
Input::Gamepads.Add(New<GDKGamepad>(device));
Input::OnGamepadsChanged();
}
void CALLBACK OnDeviceEnumerated(
_In_ GameInputCallbackToken callbackToken,
_In_ void* context,
_In_ IGameInputDevice* device,
_In_ uint64_t timestamp,
_In_ GameInputDeviceStatus currentStatus,
_In_ GameInputDeviceStatus previousStatus)
{
TryAddGamepad(device);
}
void GDKInput::Init()
{
GameInputCreate(&GameInput);
// Find connected devices
GameInputCallbackToken token;
if (SUCCEEDED(GameInput->RegisterDeviceCallback(
nullptr,
GameInputKindGamepad,
GameInputDeviceAnyStatus,
GameInputBlockingEnumeration,
nullptr,
OnDeviceEnumerated,
&token)))
{
GameInput->UnregisterCallback(token, 5000);
}
}
void GDKInput::Exit()
{
if (PrevReading)
{
PrevReading->Release();
PrevReading = nullptr;
}
if (GameInput)
{
GameInput->Release();
GameInput = nullptr;
}
}
void GDKInput::Update()
{
while (true)
{
// Read input
IGameInputReading* reading = nullptr;
if (!PrevReading)
{
if (FAILED(GDKInputImpl::GameInput->GetCurrentReading(GameInputKindGamepad, nullptr, &reading)))
{
break;
}
PrevReading = reading;
}
else
{
const HRESULT hr = GameInput->GetNextReading(PrevReading, GameInputKindGamepad, nullptr, &reading);
if (SUCCEEDED(hr))
{
PrevReading->Release();
PrevReading = reading;
}
else if (hr != GAMEINPUT_E_READING_NOT_FOUND)
{
PrevReading->Release();
PrevReading = nullptr;
break;
}
}
if (!reading)
break;
// Check if new device was connected
IGameInputDevice* device;
reading->GetDevice(&device);
TryAddGamepad(device);
device->Release();
}
}
void GDKGamepad::SetVibration(const GamepadVibrationState& state)
{
GameInputRumbleParams vibration;
vibration.lowFrequency = state.LeftSmall;
vibration.highFrequency = state.RightSmall;
vibration.leftTrigger = state.LeftLarge;
vibration.rightTrigger = state.RightLarge;
Device->SetRumbleState(&vibration);
}
bool GDKGamepad::UpdateState()
{
// Gather device state
IGameInputReading* reading = nullptr;
if (FAILED(GDKInputImpl::GameInput->GetCurrentReading(GameInputKindGamepad,Device, &reading)))
{
return true;
}
GameInputGamepadState state;
if (!reading->GetGamepadState(&state))
{
return true;
}
const float deadZone = 0.2f;
// Process buttons state
_state.Buttons[(int32)GamepadButton::A] = state.buttons & GameInputGamepadA;
_state.Buttons[(int32)GamepadButton::B] = state.buttons & GameInputGamepadB;
_state.Buttons[(int32)GamepadButton::X] = state.buttons & GameInputGamepadX;
_state.Buttons[(int32)GamepadButton::Y] = state.buttons & GameInputGamepadY;
_state.Buttons[(int32)GamepadButton::LeftShoulder] = state.buttons & GameInputGamepadLeftShoulder;
_state.Buttons[(int32)GamepadButton::RightShoulder] = state.buttons & GameInputGamepadRightShoulder;
_state.Buttons[(int32)GamepadButton::Back] = state.buttons & GameInputGamepadMenu;
_state.Buttons[(int32)GamepadButton::Start] = state.buttons & GameInputGamepadView;
_state.Buttons[(int32)GamepadButton::LeftThumb] = state.buttons & GameInputGamepadLeftThumbstick;
_state.Buttons[(int32)GamepadButton::RightThumb] = state.buttons & GameInputGamepadRightThumbstick;
_state.Buttons[(int32)GamepadButton::LeftTrigger] = state.leftTrigger > deadZone;
_state.Buttons[(int32)GamepadButton::RightTrigger] = state.rightTrigger > deadZone;
_state.Buttons[(int32)GamepadButton::DPadUp] = state.buttons & GameInputGamepadDPadUp;
_state.Buttons[(int32)GamepadButton::DPadDown] = state.buttons & GameInputGamepadDPadDown;
_state.Buttons[(int32)GamepadButton::DPadLeft] = state.buttons & GameInputGamepadDPadLeft;
_state.Buttons[(int32)GamepadButton::DPadRight] = state.buttons & GameInputGamepadDPadRight;
_state.Buttons[(int32)GamepadButton::LeftStickUp] = state.leftThumbstickY > deadZone;
_state.Buttons[(int32)GamepadButton::LeftStickDown] = state.leftThumbstickY < -deadZone;
_state.Buttons[(int32)GamepadButton::LeftStickLeft] = state.leftThumbstickX < -deadZone;
_state.Buttons[(int32)GamepadButton::LeftStickRight] = state.leftThumbstickX > deadZone;
_state.Buttons[(int32)GamepadButton::RightStickUp] = state.rightThumbstickY > deadZone;
_state.Buttons[(int32)GamepadButton::RightStickDown] = state.rightThumbstickY < -deadZone;
_state.Buttons[(int32)GamepadButton::RightStickLeft] = state.rightThumbstickX < -deadZone;
_state.Buttons[(int32)GamepadButton::RightStickRight] = state.rightThumbstickX > deadZone;
// Process axes state
_state.Axis[(int32)GamepadAxis::LeftStickX] = state.leftThumbstickX;
_state.Axis[(int32)GamepadAxis::LeftStickY] = state.leftThumbstickY;
_state.Axis[(int32)GamepadAxis::RightStickX] = state.rightThumbstickX;
_state.Axis[(int32)GamepadAxis::RightStickY] = state.rightThumbstickY;
_state.Axis[(int32)GamepadAxis::LeftTrigger] = state.leftTrigger;
_state.Axis[(int32)GamepadAxis::RightTrigger] = state.rightTrigger;
reading->Release();
return false;
}
#else
void GDKInput::Init()
{
}
void GDKInput::Exit()
{
}
void GDKInput::Update()
{
}
#endif

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_GDK
/// <summary>
/// GDK platform specific implementation of the input system parts.
/// </summary>
class GDKInput
{
public:
static void Init();
static void Exit();
static void Update();
};
#endif

View File

@@ -0,0 +1,631 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_GDK
#include "Engine/Platform/Platform.h"
#include "Engine/Platform/Window.h"
#include "Engine/Platform/CreateWindowSettings.h"
#include "Engine/Platform/WindowsManager.h"
#include "Engine/Platform/MemoryStats.h"
#include "Engine/Platform/BatteryInfo.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Platform/MessageBox.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Utilities/StringConverter.h"
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
#include "GDKInput.h"
#include <XGameRuntime.h>
#include <appnotify.h>
inline bool operator==(const APP_LOCAL_DEVICE_ID& l, const APP_LOCAL_DEVICE_ID& r)
{
return Platform::MemoryCompare(&l, &r, sizeof(APP_LOCAL_DEVICE_ID)) == 0;
}
const Char* GDKPlatform::ApplicationWindowClass = TEXT("FlaxWindow");
void* GDKPlatform::Instance = nullptr;
Delegate<> GDKPlatform::OnSuspend;
Delegate<> GDKPlatform::OnResume;
struct User
{
XUserHandle UserHandle;
XUserLocalId LocalId;
Array<APP_LOCAL_DEVICE_ID, FixedAllocation<32>> AssociatedDevices;
void Set(XUserHandle userHandle, XUserLocalId localId)
{
UserHandle = userHandle;
LocalId = localId;
AssociatedDevices.Clear();
}
void Unset()
{
XUserCloseHandle(UserHandle);
AssociatedDevices.Clear();
}
};
namespace
{
bool IsSuspended = false;
HANDLE PlmSuspendComplete = nullptr;
HANDLE PlmSignalResume = nullptr;
PAPPSTATE_REGISTRATION Plm = {};
String UserLocale, ComputerName;
XTaskQueueHandle TaskQueue = nullptr;
Array<User, FixedAllocation<8>> Users;
XTaskQueueRegistrationToken UserChangeEventCallbackToken;
XTaskQueueRegistrationToken UserDeviceAssociationChangedCallbackToken;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_USER:
{
LOG(Info, "Suspending application");
IsSuspended = true;
GDKPlatform::OnSuspend();
// Complete deferral
SetEvent(PlmSuspendComplete);
(void)WaitForSingleObject(PlmSignalResume, INFINITE);
IsSuspended = false;
LOG(Info, "Resuming application");
GDKPlatform::OnResume();
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
// Find window to process that message
if (hWnd != nullptr)
{
// Find window by handle
const auto win = WindowsManager::GetByNativePtr(hWnd);
if (win)
{
return static_cast<GDKWindow*>(win)->WndProc(msg, wParam, lParam);
}
}
// Default
return DefWindowProc(hWnd, msg, wParam, lParam);
}
void CALLBACK UserChangeEventCallback(_In_opt_ void* context,_In_ XUserLocalId userLocalId, _In_ XUserChangeEvent event)
{
LOG(Info, "User event (userLocalId: {0}, event: {1})", userLocalId.value, (int32)event);
switch (event)
{
case XUserChangeEvent::SignedInAgain:
break;
case XUserChangeEvent::SignedOut:
for (int32 i = 0; i < Users.Count(); i++)
{
if (Users[i].LocalId.value == userLocalId.value)
{
Users[i].Unset();
Users.RemoveAt(i);
break;
}
}
break;
default: ;
}
}
String ToString(const APP_LOCAL_DEVICE_ID& deviceId)
{
return String::Format(TEXT("{}-{}-{}-{}-{}-{}-{}-{}"),
*reinterpret_cast<const unsigned int*>(&deviceId.value[0]),
*reinterpret_cast<const unsigned int*>(&deviceId.value[4]),
*reinterpret_cast<const unsigned int*>(&deviceId.value[8]),
*reinterpret_cast<const unsigned int*>(&deviceId.value[12]),
*reinterpret_cast<const unsigned int*>(&deviceId.value[16]),
*reinterpret_cast<const unsigned int*>(&deviceId.value[20]),
*reinterpret_cast<const unsigned int*>(&deviceId.value[24]),
*reinterpret_cast<const unsigned int*>(&deviceId.value[28]));
}
User* FindUser(const XUserLocalId& id)
{
User* result = nullptr;
for (auto& user : Users)
{
if (user.LocalId.value == id.value)
{
result = &user;
break;
}
}
return result;
}
void CALLBACK UserDeviceAssociationChangedCallback(_In_opt_ void* context,_In_ const XUserDeviceAssociationChange* change)
{
LOG(Info, "User device association event (deviceId: {0}, oldUser: {1}, newUser: {2})", ToString(change->deviceId), change->oldUser.value, change->newUser.value);
User* oldGameUser = FindUser(change->oldUser);
if (oldGameUser)
{
oldGameUser->AssociatedDevices.Remove(change->deviceId);
}
User* newGameUser = FindUser(change->newUser);
if (newGameUser)
{
newGameUser->AssociatedDevices.Add(change->deviceId);
}
}
void OnMainWindowCreated(HWND hWnd)
{
// Register for app suspending/resuming events
PlmSuspendComplete = CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
PlmSignalResume = CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE);
if (!PlmSuspendComplete || !PlmSignalResume)
return;
if (RegisterAppStateChangeNotification([](BOOLEAN quiesced, PVOID context)
{
if (quiesced)
{
ResetEvent(PlmSuspendComplete);
ResetEvent(PlmSignalResume);
// To ensure we use the main UI thread to process the notification, we self-post a message
PostMessage(reinterpret_cast<HWND>(context), WM_USER, 0, 0);
// To defer suspend, you must wait to exit this callback
(void)WaitForSingleObject(PlmSuspendComplete, INFINITE);
}
else
{
SetEvent(PlmSignalResume);
}
}, hWnd, &Plm))
return;
}
void CALLBACK AddUserComplete(_In_ XAsyncBlock* ab)
{
XUserHandle userHandle;
HRESULT hr = XUserAddResult(ab, &userHandle);
if (SUCCEEDED(hr))
{
XUserLocalId userLocalId;
XUserGetLocalId(userHandle, &userLocalId);
XUserLocalId localId;
XUserGetLocalId(userHandle, &localId);
if (FindUser(localId) == nullptr)
{
// Add user
auto& user = Users.AddOne();
user.Set(userHandle, userLocalId);
}
}
delete ab;
}
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
{
const char *firstButtonText, *secondButtonText, *thirdButtonText;
XGameUiMessageDialogButton defaultButton, cancelButton = XGameUiMessageDialogButton::First;
switch (buttons)
{
case MessageBoxButtons::AbortRetryIgnore:
firstButtonText = "Abort";
secondButtonText = "Retry";
thirdButtonText = "Ignore";
defaultButton = XGameUiMessageDialogButton::Second;
cancelButton = XGameUiMessageDialogButton::Third;
break;
case MessageBoxButtons::OK:
firstButtonText = "OK";
secondButtonText = nullptr;
thirdButtonText = nullptr;
defaultButton = XGameUiMessageDialogButton::First;
cancelButton = XGameUiMessageDialogButton::First;
break;
case MessageBoxButtons::OKCancel:
firstButtonText = "OK";
secondButtonText = "Cancel";
thirdButtonText = nullptr;
defaultButton = XGameUiMessageDialogButton::First;
cancelButton = XGameUiMessageDialogButton::Second;
break;
case MessageBoxButtons::RetryCancel:
firstButtonText = "Retry";
secondButtonText = "Cancel";
thirdButtonText = nullptr;
defaultButton = XGameUiMessageDialogButton::First;
cancelButton = XGameUiMessageDialogButton::Second;
break;
case MessageBoxButtons::YesNo:
firstButtonText = "Yes";
secondButtonText = "No";
thirdButtonText = nullptr;
defaultButton = XGameUiMessageDialogButton::First;
cancelButton = XGameUiMessageDialogButton::Second;
break;
case MessageBoxButtons::YesNoCancel:
firstButtonText = "Yes";
secondButtonText = "No";
thirdButtonText = "Cancel";
defaultButton = XGameUiMessageDialogButton::First;
cancelButton = XGameUiMessageDialogButton::Third;
break;
default:
return DialogResult::None;
}
const StringAsANSI<> textAnsi(text.Get(), text.Length());
const StringAsANSI<> captionAnsi(caption.Get(), caption.Length());
// Show dialog and wait for the result
DialogResult result = DialogResult::None;
XTaskQueueHandle queue;
if (FAILED(XTaskQueueCreate(XTaskQueueDispatchMode::ThreadPool, XTaskQueueDispatchMode::Immediate, &queue)))
{
return DialogResult::None;
}
XAsyncBlock* ab = new XAsyncBlock();
Platform::MemoryClear(ab, sizeof(XAsyncBlock));
ab->queue = queue;
XGameUiMessageDialogButton button;
if (SUCCEEDED(XGameUiShowMessageDialogAsync(ab, captionAnsi.Get(), textAnsi.Get(), firstButtonText, secondButtonText, thirdButtonText, defaultButton, cancelButton)) &&
SUCCEEDED(XAsyncGetStatus(ab, true)) &&
SUCCEEDED(XGameUiShowMessageDialogResult(ab, &button)))
{
switch (buttons)
{
case MessageBoxButtons::AbortRetryIgnore:
result = button == XGameUiMessageDialogButton::First ? DialogResult::Abort : button == XGameUiMessageDialogButton::Second ? DialogResult::Retry : DialogResult::Ignore;
break;
case MessageBoxButtons::OK:
result = DialogResult::OK;
break;
case MessageBoxButtons::OKCancel:
result = button == XGameUiMessageDialogButton::First ? DialogResult::OK : DialogResult::Cancel;
break;
case MessageBoxButtons::RetryCancel:
result = button == XGameUiMessageDialogButton::First ? DialogResult::Retry : DialogResult::Cancel;
break;
case MessageBoxButtons::YesNo:
result = button == XGameUiMessageDialogButton::First ? DialogResult::Yes : DialogResult::No;
break;
case MessageBoxButtons::YesNoCancel:
result = button == XGameUiMessageDialogButton::First ? DialogResult::Yes : button == XGameUiMessageDialogButton::Second ? DialogResult::No : DialogResult::Cancel;
break;
}
}
// Cleanup
XTaskQueueTerminate(queue, true, nullptr, nullptr);
delete ab;
return result;
}
void GDKPlatform::PreInit(void* hInstance)
{
ASSERT(hInstance);
Instance = hInstance;
// Initialize the Game Runtime APIs
if (FAILED(XGameRuntimeInitialize()))
{
Error(TEXT("Game runtime initialization failed!"));
exit(-1);
}
// Register window class
WNDCLASSW windowsClass;
Platform::MemoryClear(&windowsClass, sizeof(WNDCLASS));
windowsClass.style = CS_HREDRAW | CS_VREDRAW;
windowsClass.lpfnWndProc = WndProc;
windowsClass.hInstance = (HINSTANCE)Instance;
windowsClass.lpszClassName = ApplicationWindowClass;
if (!RegisterClassW(&windowsClass))
{
Error(TEXT("Window class registration failed!"));
exit(-1);
}
}
bool GDKPlatform::IsRunningOnDevKit()
{
const XSystemDeviceType deviceType = XSystemGetDeviceType();
return deviceType == XSystemDeviceType::XboxOneXDevkit || deviceType == XSystemDeviceType::XboxScarlettDevkit;
}
bool GDKPlatform::Init()
{
if (Win32Platform::Init())
return true;
DWORD tmp;
Char buffer[256];
// Get user locale string
if (GetUserDefaultLocaleName(buffer, LOCALE_NAME_MAX_LENGTH))
{
UserLocale = String(buffer);
}
// Get computer name string
if (GetComputerNameW(buffer, &tmp))
{
ComputerName = String(buffer);
}
// Create a task queue that will process in the background on system threads and fire callbacks on a thread we choose in a serialized order
if (FAILED(XTaskQueueCreate(XTaskQueueDispatchMode::ThreadPool, XTaskQueueDispatchMode::Manual, &TaskQueue)))
return true;
// Register for any change events for user
XUserRegisterForChangeEvent(
TaskQueue,
nullptr,
&UserChangeEventCallback,
&UserChangeEventCallbackToken
);
// Registers for any change to device association so that the application can keep up-to-date information about users and their associated devices
XUserRegisterForDeviceAssociationChanged(
TaskQueue,
nullptr,
&UserDeviceAssociationChangedCallback,
&UserDeviceAssociationChangedCallbackToken
);
// Login the default user
{
auto asyncBlock = new XAsyncBlock();
asyncBlock->queue = TaskQueue;
asyncBlock->callback = AddUserComplete;
HRESULT hr = XUserAddAsync(XUserAddOptions::AddDefaultUserAllowingUI, asyncBlock);
if (FAILED(hr))
delete asyncBlock;
}
GDKInput::Init();
return false;
}
void GDKPlatform::BeforeRun()
{
// Log system info
const XSystemAnalyticsInfo analyticsInfo = XSystemGetAnalyticsInfo();
LOG(Info, "{0}, {1}", StringAsUTF16<64>(analyticsInfo.family).Get(), StringAsUTF16<64>(analyticsInfo.form).Get());
LOG(Info, "OS Version {0}.{1}.{2}.{3}", analyticsInfo.osVersion.major, analyticsInfo.osVersion.minor, analyticsInfo.osVersion.build, analyticsInfo.osVersion.revision);
}
void GDKPlatform::Tick()
{
PROFILE_CPU_NAMED("Application.Tick");
GDKInput::Update();
// Handle callbacks in the main thread to ensure thread safety
while (XTaskQueueDispatch(TaskQueue, XTaskQueuePort::Completion, 0))
{
}
// Check to see if any messages are waiting in the queue
MSG msg;
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
// Translate the message and dispatch it to WindowProc()
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void GDKPlatform::BeforeExit()
{
}
void GDKPlatform::Exit()
{
GDKInput::Exit();
XUserUnregisterForDeviceAssociationChanged(UserDeviceAssociationChangedCallbackToken, false);
XUserUnregisterForChangeEvent(UserChangeEventCallbackToken, false);
if (TaskQueue)
{
XTaskQueueCloseHandle(TaskQueue);
}
UnregisterAppStateChangeNotification(Plm);
CloseHandle(PlmSuspendComplete);
CloseHandle(PlmSignalResume);
UnregisterClassW(ApplicationWindowClass, nullptr);
XGameRuntimeUninitialize();
}
#if !BUILD_RELEASE
void GDKPlatform::Log(const StringView& msg)
{
OutputDebugStringW(msg.Get());
OutputDebugStringW(TEXT(PLATFORM_LINE_TERMINATOR));
}
bool GDKPlatform::IsDebuggerPresent()
{
return !!::IsDebuggerPresent();
}
#endif
BatteryInfo GDKPlatform::GetBatteryInfo()
{
BatteryInfo info;
info.State = BatteryInfo::States::Connected;
return info;
}
int32 GDKPlatform::GetDpi()
{
return 96;
}
String GDKPlatform::GetUserLocaleName()
{
return UserLocale;
}
String GDKPlatform::GetComputerName()
{
return ComputerName;
}
String GDKPlatform::GetUserName()
{
return String::Empty;
}
bool GDKPlatform::GetHasFocus()
{
return !IsSuspended;
}
bool GDKPlatform::CanOpenUrl(const StringView& url)
{
return Users.HasItems();
}
void GDKPlatform::OpenUrl(const StringView& url)
{
const StringAsANSI<> urlANSI(url.Get(), url.Length());
XLaunchUri(Users[0].UserHandle, urlANSI.Get());
}
struct GetMonitorBoundsData
{
Vector2 Pos;
Rectangle Result;
GetMonitorBoundsData(const Vector2& pos)
: Pos(pos)
, Result(Vector2::Zero, GDKPlatform::GetDesktopSize())
{
}
};
Rectangle GDKPlatform::GetMonitorBounds(const Vector2& screenPos)
{
return Rectangle(Vector2::Zero, GetDesktopSize());
}
Vector2 GDKPlatform::GetDesktopSize()
{
return Vector2(1920, 1080);
}
Rectangle GDKPlatform::GetVirtualDesktopBounds()
{
return Rectangle(Vector2::Zero, GetDesktopSize());
}
void GDKPlatform::GetEnvironmentVariables(Dictionary<String, String>& result)
{
const LPWCH environmentStr = GetEnvironmentStringsW();
if (environmentStr)
{
LPWCH env = environmentStr;
while (*env != '\0')
{
if (*env != '=')
{
WCHAR* str = wcschr(env, '=');
ASSERT(str);
result[String(env, (int32)(str - env))] = str + 1;
}
while (*env != '\0')
env++;
env++;
}
FreeEnvironmentStringsW(environmentStr);
}
}
bool GDKPlatform::GetEnvironmentVariable(const String& name, String& value)
{
const int32 bufferSize = 512;
Char buffer[bufferSize];
DWORD result = GetEnvironmentVariableW(*name, buffer, bufferSize);
if (result == 0)
{
LOG_WIN32_LAST_ERROR;
return true;
}
if (bufferSize < result)
{
value.ReserveSpace(result);
result = GetEnvironmentVariableW(*name, *value, result);
if (!result)
{
LOG_WIN32_LAST_ERROR;
return FALSE;
}
}
else
{
value.Set(buffer, result);
}
return false;
}
bool GDKPlatform::SetEnvironmentVariable(const String& name, const String& value)
{
if (!SetEnvironmentVariableW(*name, *value))
{
LOG_WIN32_LAST_ERROR;
return true;
}
return false;
}
Window* GDKPlatform::CreateWindow(const CreateWindowSettings& settings)
{
return New<GDKWindow>(settings);
}
void* GDKPlatform::LoadLibrary(const Char* filename)
{
ASSERT(filename);
void* handle = ::LoadLibraryW(filename);
if (!handle)
{
LOG(Warning, "Failed to load '{0}' (GetLastError={1})", filename, GetLastError());
}
return handle;
}
void GDKPlatform::FreeLibrary(void* handle)
{
::FreeLibrary((HMODULE)handle);
}
void* GDKPlatform::GetProcAddress(void* handle, const char* symbol)
{
return (void*)::GetProcAddress((HMODULE)handle, symbol);
}
#endif

View File

@@ -0,0 +1,79 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_GDK
#include "Engine/Platform/Win32/Win32Platform.h"
/// <summary>
/// The GDK platform implementation and application management utilities.
/// </summary>
class GDKPlatform : public Win32Platform
{
public:
/// <summary>
/// Win32 application windows class name.
/// </summary>
static const Char* ApplicationWindowClass;
/// <summary>
/// Handle to Win32 application instance.
/// </summary>
static void* Instance;
static Delegate<> OnSuspend;
static Delegate<> OnResume;
public:
/// <summary>
/// Returns true if current OS version is Windows 10.
/// </summary>
static bool IsWindows10()
{
return true;
}
/// <summary>
/// Pre initialize platform.
/// </summary>
/// <param name="hInstance">The Win32 application instance.</param>
static void PreInit(void* hInstance);
static bool IsRunningOnDevKit();
public:
// [Win32Platform]
static bool Init();
static void BeforeRun();
static void Tick();
static void BeforeExit();
static void Exit();
#if !BUILD_RELEASE
static void Log(const StringView& msg);
static bool IsDebuggerPresent();
#endif
static BatteryInfo GetBatteryInfo();
static int32 GetDpi();
static String GetUserLocaleName();
static String GetComputerName();
static String GetUserName();
static bool GetHasFocus();
static bool CanOpenUrl(const StringView& url);
static void OpenUrl(const StringView& url);
static Rectangle GetMonitorBounds(const Vector2& screenPos);
static Vector2 GetDesktopSize();
static Rectangle GetVirtualDesktopBounds();
static void GetEnvironmentVariables(Dictionary<String, String, HeapAllocation>& result);
static bool GetEnvironmentVariable(const String& name, String& value);
static bool SetEnvironmentVariable(const String& name, const String& value);
static Window* CreateWindow(const CreateWindowSettings& settings);
static void* LoadLibrary(const Char* filename);
static void FreeLibrary(void* handle);
static void* GetProcAddress(void* handle, const char* symbol);
};
#endif

View File

@@ -0,0 +1,394 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_GDK
#undef _GAMING_XBOX
#include "GDKWindow.h"
#include "GDKPlatform.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Graphics/GPUSwapChain.h"
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Graphics/GPUDevice.h"
#include "Engine/Platform/Win32/IncludeWindowsHeaders.h"
extern void OnMainWindowCreated(HWND hWnd);
GDKWindow::GDKWindow(const CreateWindowSettings& settings)
: WindowBase(settings)
{
int32 x = Math::TruncToInt(settings.Position.X);
int32 y = Math::TruncToInt(settings.Position.Y);
int32 clientWidth = Math::TruncToInt(settings.Size.X);
int32 clientHeight = Math::TruncToInt(settings.Size.Y);
int32 windowWidth = clientWidth;
int32 windowHeight = clientHeight;
_clientSize = Vector2((float)clientWidth, (float)clientHeight);
// Setup window style
uint32 style = WS_POPUP, exStyle = 0;
if (settings.SupportsTransparency)
exStyle |= WS_EX_LAYERED;
if (!settings.ActivateWhenFirstShown)
exStyle |= WS_EX_NOACTIVATE;
if (settings.ShowInTaskbar)
exStyle |= WS_EX_APPWINDOW;
else
exStyle |= WS_EX_TOOLWINDOW;
if (settings.IsTopmost)
exStyle |= WS_EX_TOPMOST;
if (!settings.AllowInput)
exStyle |= WS_EX_TRANSPARENT;
if (settings.AllowMaximize)
style |= WS_MAXIMIZEBOX;
if (settings.AllowMinimize)
style |= WS_MINIMIZEBOX;
if (settings.HasSizingFrame)
style |= WS_THICKFRAME;
// Check if window should have a border
if (settings.HasBorder)
{
// Create window style flags
style |= WS_OVERLAPPED | WS_SYSMENU | WS_BORDER | WS_CAPTION;
exStyle |= 0;
// Adjust window size and positions to take into account window border
RECT winRect = { 0, 0, clientWidth, clientHeight };
AdjustWindowRectEx(&winRect, style, FALSE, exStyle);
x += winRect.left;
y += winRect.top;
windowWidth = winRect.right - winRect.left;
windowHeight = winRect.bottom - winRect.top;
}
else
{
// Create window style flags
style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
exStyle |= WS_EX_WINDOWEDGE;
}
// Creating the window
_handle = CreateWindowExW(
exStyle,
Platform::ApplicationWindowClass,
settings.Title.GetText(),
style,
x,
y,
windowWidth,
windowHeight,
settings.Parent ? static_cast<HWND>(settings.Parent->GetNativePtr()) : nullptr,
nullptr,
(HINSTANCE)Platform::Instance,
nullptr);
// Validate result
if (!HasHWND())
{
LOG_WIN32_LAST_ERROR;
Platform::Fatal(TEXT("Cannot create window."));
}
OnMainWindowCreated(_handle);
}
GDKWindow::~GDKWindow()
{
if (HasHWND())
{
// Destroy window
if (DestroyWindow(_handle) == 0)
{
LOG(Warning, "DestroyWindow failed! Error: {0:#x}", GetLastError());
}
// Clear
_handle = nullptr;
_visible = false;
}
}
void* GDKWindow::GetNativePtr() const
{
return _handle;
}
void GDKWindow::Show()
{
if (!_visible)
{
InitSwapChain();
if (_showAfterFirstPaint)
{
if (RenderTask)
RenderTask->Enabled = true;
return;
}
ASSERT(HasHWND());
// Show
ShowWindow(_handle, (_settings.AllowInput && _settings.ActivateWhenFirstShown) ? SW_SHOW : SW_SHOWNA);
// Base
WindowBase::Show();
_focused = true; // TODO: remove it and check if the initial focus works when game starts with rendering, do we get WM_ACTIVATEAPP?
}
}
void GDKWindow::Hide()
{
if (_visible)
{
ASSERT(HasHWND());
// Hide
ShowWindow(_handle, SW_HIDE);
// Base
WindowBase::Hide();
}
}
void GDKWindow::Minimize()
{
ASSERT(HasHWND());
ShowWindow(_handle, SW_MINIMIZE);
}
void GDKWindow::Maximize()
{
ASSERT(HasHWND());
ShowWindow(_handle, SW_MAXIMIZE);
}
void GDKWindow::Restore()
{
ASSERT(HasHWND());
ShowWindow(_handle, SW_RESTORE);
}
bool GDKWindow::IsClosed() const
{
return !HasHWND();
}
bool GDKWindow::IsForegroundWindow() const
{
return Platform::GetHasFocus();
}
void GDKWindow::SetIsFullscreen(bool isFullscreen)
{
}
void GDKWindow::GetScreenInfo(int32& x, int32& y, int32& width, int32& height) const
{
x = 0;
y = 0;
width = (int32)_clientSize.X;
height = (int32)_clientSize.Y;
}
void GDKWindow::SetCursor(CursorType type)
{
// Base
WindowBase::SetCursor(type);
UpdateCursor();
}
void GDKWindow::CheckForWindowResize()
{
// Skip for minimized window (GetClientRect for minimized window returns 0)
if (_minimized)
return;
ASSERT(HasHWND());
// Cache client size
RECT rect;
GetClientRect(_handle, &rect);
const int32 width = Math::Max(rect.right - rect.left, 0L);
const int32 height = Math::Max(rect.bottom - rect.top, 0L);
_clientSize = Vector2(static_cast<float>(width), static_cast<float>(height));
// Check if window size has been changed
if (width > 0 && height > 0 && (_swapChain == nullptr || width != _swapChain->GetWidth() || height != _swapChain->GetHeight()))
{
OnResize(width, height);
}
}
void GDKWindow::UpdateCursor() const
{
if (_cursor == CursorType::Hidden)
{
::SetCursor(nullptr);
return;
}
int32 index = 0;
switch (_cursor)
{
case CursorType::Default:
break;
case CursorType::Cross:
index = 1;
break;
case CursorType::Hand:
index = 2;
break;
case CursorType::Help:
index = 3;
break;
case CursorType::IBeam:
index = 4;
break;
case CursorType::No:
index = 5;
break;
case CursorType::Wait:
index = 11;
break;
case CursorType::SizeAll:
index = 6;
break;
case CursorType::SizeNESW:
index = 7;
break;
case CursorType::SizeNS:
index = 8;
break;
case CursorType::SizeNWSE:
index = 9;
break;
case CursorType::SizeWE:
index = 10;
break;
}
static const LPCWSTR cursors[] =
{
IDC_ARROW,
IDC_CROSS,
IDC_HAND,
IDC_HELP,
IDC_IBEAM,
IDC_NO,
IDC_SIZEALL,
IDC_SIZENESW,
IDC_SIZENS,
IDC_SIZENWSE,
IDC_SIZEWE,
IDC_WAIT,
};
ASSERT(index >= 0 && index < ARRAY_COUNT(cursors));
const HCURSOR cursor = LoadCursorW(nullptr, cursors[index]);
::SetCursor(cursor);
}
Windows::LRESULT GDKWindow::WndProc(Windows::UINT msg, Windows::WPARAM wParam, Windows::LPARAM lParam)
{
switch (msg)
{
case WM_SETCURSOR:
{
if (LOWORD(lParam) == HTCLIENT)
{
UpdateCursor();
return true;
}
break;
}
case WM_CREATE:
{
return 0;
}
case WM_SIZE:
{
if (SIZE_MINIMIZED == wParam)
{
// Set flags
_minimized = true;
_maximized = false;
}
else
{
RECT rcCurrentClient;
GetClientRect(_handle, &rcCurrentClient);
if (rcCurrentClient.top == 0 && rcCurrentClient.bottom == 0)
{
// Rapidly clicking the task bar to minimize and restore a window can cause a WM_SIZE message with SIZE_RESTORED when
// the window has actually become minimized due to rapid change so just ignore this message.
}
else if (SIZE_MAXIMIZED == wParam)
{
// Set flags
_minimized = false;
_maximized = true;
// Check size
CheckForWindowResize();
}
else if (SIZE_RESTORED == wParam)
{
if (_maximized)
{
// Clear flag
_maximized = false;
// Check size
CheckForWindowResize();
}
else if (_minimized)
{
// Clear flag
_minimized = false;
// Check size
CheckForWindowResize();
}
else
{
// This WM_SIZE come from resizing the window via an API like SetWindowPos() so resize
CheckForWindowResize();
}
}
}
break;
}
case WM_SETFOCUS:
OnGotFocus();
break;
case WM_KILLFOCUS:
OnLostFocus();
break;
case WM_ACTIVATEAPP:
if (wParam == TRUE && !_focused)
{
OnGotFocus();
}
else if (wParam == FALSE && _focused)
{
OnLostFocus();
}
break;
case WM_CLOSE:
Close(ClosingReason::User);
return 0;
case WM_DESTROY:
{
// Quit
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(_handle, msg, wParam, lParam);
}
#endif

View File

@@ -0,0 +1,93 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_GDK
#include "Engine/Platform/Base/WindowBase.h"
#include "Engine/Platform/Platform.h"
#include "Engine/Platform/Win32/WindowsMinimal.h"
/// <summary>
/// Implementation of the window class for GDK platform.
/// </summary>
class GDKWindow : public WindowBase
{
friend GDKPlatform;
private:
Windows::HWND _handle;
Vector2 _clientSize;
public:
/// <summary>
/// Initializes a new instance of the <see cref="GDKWindow"/> class.
/// </summary>
/// <param name="settings">The initial window settings.</param>
GDKWindow(const CreateWindowSettings& settings);
/// <summary>
/// Finalizes an instance of the <see cref="GDKWindow"/> class.
/// </summary>
~GDKWindow();
public:
/// <summary>
/// Gets the window handle.
/// </summary>
FORCE_INLINE Windows::HWND GetHWND() const
{
return _handle;
}
/// <summary>
/// Checks if the window has valid handle created.
/// </summary>
FORCE_INLINE bool HasHWND() const
{
return _handle != nullptr;
}
/// <summary>
/// Gets the information about screen which contains window.
/// </summary>
/// <param name="x">The x position.</param>
/// <param name="y">The y position.</param>
/// <param name="width">The width.</param>
/// <param name="height">The height.</param>
void GetScreenInfo(int32& x, int32& y, int32& width, int32& height) const;
public:
/// <summary>
/// The Windows messages procedure.
/// </summary>
/// <param name="msg">The mMessage.</param>
/// <param name="wParam">The first parameter.</param>
/// <param name="lParam">The second parameter.</param>
/// <returns>The result.</returns>
Windows::LRESULT WndProc(Windows::UINT msg, Windows::WPARAM wParam, Windows::LPARAM lParam);
private:
void CheckForWindowResize();
void UpdateCursor() const;
public:
// [Window]
void* GetNativePtr() const override;
void Show() override;
void Hide() override;
void Minimize() override;
void Maximize() override;
void Restore() override;
bool IsClosed() const override;
bool IsForegroundWindow() const override;
void SetIsFullscreen(bool isFullscreen) override;
void SetCursor(CursorType type) override;
};
#endif

View File

@@ -58,10 +58,12 @@ public class Platform : EngineModule
break;
case TargetPlatform.XboxOne:
options.SourcePaths.Add(Path.Combine(FolderPath, "Win32"));
options.SourcePaths.Add(Path.Combine(FolderPath, "GDK"));
options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxOne", "Engine", "Platform"));
break;
case TargetPlatform.XboxScarlett:
options.SourcePaths.Add(Path.Combine(FolderPath, "Win32"));
options.SourcePaths.Add(Path.Combine(FolderPath, "GDK"));
options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxScarlett", "Engine", "Platform"));
break;
case TargetPlatform.Android:

View File

@@ -111,8 +111,8 @@ class XboxOnePlatform;
typedef XboxOnePlatform Platform;
class Win32Thread;
typedef Win32Thread Thread;
class XboxOneWindow;
typedef XboxOneWindow Window;
class GDKWindow;
typedef GDKWindow Window;
class NetworkBase;
typedef NetworkBase Network;
@@ -134,8 +134,8 @@ class XboxScarlettPlatform;
typedef XboxScarlettPlatform Platform;
class Win32Thread;
typedef Win32Thread Thread;
class XboxScarlettWindow;
typedef XboxScarlettWindow Window;
class GDKWindow;
typedef GDKWindow Window;
class NetworkBase;
typedef NetworkBase Network;

View File

@@ -11,9 +11,9 @@
#elif PLATFORM_PS4
#include "Platforms/PS4/Engine/Platform/PS4Window.h"
#elif PLATFORM_XBOX_ONE
#include "Platforms/XboxOne/Engine/Platform/XboxOneWindow.h"
#include "GDK/GDKWindow.h"
#elif PLATFORM_XBOX_SCARLETT
#include "Platforms/XboxScarlett/Engine/Platform/XboxScarlettWindow.h"
#include "GDK/GDKWindow.h"
#elif PLATFORM_ANDROID
#include "Android/AndroidWindow.h"
#elif PLATFORM_SWITCH

View File

@@ -0,0 +1,69 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using System.IO;
using System.Linq;
namespace Flax.Build.Platforms
{
/// <summary>
/// The Microsoft Game Development Kit.
/// </summary>
/// <seealso cref="Sdk" />
public sealed class GDK : Sdk
{
/// <summary>
/// The singleton instance.
/// </summary>
public static readonly GDK Instance = new GDK();
/// <inheritdoc />
public override TargetPlatform[] Platforms => new[]
{
TargetPlatform.Windows,
};
/// <summary>
/// Initializes a new instance of the <see cref="GDK"/> class.
/// </summary>
public GDK()
{
if (!Platforms.Contains(Platform.BuildTargetPlatform))
return;
var sdkDir = Environment.GetEnvironmentVariable("GameDKLatest");
if (!Directory.Exists(sdkDir))
sdkDir = Environment.GetEnvironmentVariable("GRDKLatest");
if (Directory.Exists(sdkDir))
{
if (sdkDir.EndsWith("GRDK\\"))
sdkDir = sdkDir.Remove(sdkDir.Length - 6);
else if (sdkDir.EndsWith("GRDK"))
sdkDir = sdkDir.Remove(sdkDir.Length - 5);
RootPath = sdkDir;
// Read the SDK version number
string sdkManifest = Path.Combine(RootPath, "GRDK", "grdk.ini");
if (File.Exists(sdkManifest))
{
var contents = File.ReadAllText(sdkManifest);
const string prefix = "_xbld_edition=";
var start = contents.IndexOf(prefix) + prefix.Length;
var end = contents.IndexOf("\r\n_xbld_full_productbuild");
var versionText = contents.Substring(start, end - start);
Version = new Version(int.Parse(versionText), 0);
var minEdition = 200500;
if (Version.Major < minEdition)
{
Log.Error(string.Format("Unsupported GDK version {0}. Minimum supported is edition {1}.", Version.Major, minEdition));
return;
}
Log.Info(string.Format("Found GDK {0} at {1}", Version.Major, RootPath));
IsValid = true;
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System.Linq;
using Flax.Build.Projects.VisualStudio;
namespace Flax.Build.Platforms
{
/// <summary>
/// The GDK platform implementation.
/// </summary>
/// <seealso cref="Platform" />
/// <seealso cref="Flax.Build.Platforms.WindowsPlatformBase" />
public abstract class GDKPlatform : WindowsPlatformBase
{
/// <summary>
/// Initializes a new instance of the <see cref="GDKPlatform"/> class.
/// </summary>
protected GDKPlatform()
{
// Visual Studio 2017 or newer required
var visualStudio = VisualStudioInstance.GetInstances().FirstOrDefault(x => x.Version == VisualStudioVersion.VisualStudio2017 || x.Version == VisualStudioVersion.VisualStudio2019);
if (visualStudio == null)
_hasRequiredSDKsInstalled = false;
// Windows 10.0.19041.0 SDK or newer required
var sdks = GetSDKs();
if (!sdks.ContainsKey(WindowsPlatformSDK.v10_0_19041_0))
_hasRequiredSDKsInstalled = false;
// v141 toolset or newer required
var toolsets = GetToolsets();
if (!toolsets.ContainsKey(WindowsPlatformToolset.v141) &&
!toolsets.ContainsKey(WindowsPlatformToolset.v142))
_hasRequiredSDKsInstalled = false;
}
}
}

View File

@@ -0,0 +1,63 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using System.IO;
using Flax.Build.NativeCpp;
namespace Flax.Build.Platforms
{
/// <summary>
/// The GDK toolchain implementation.
/// </summary>
/// <seealso cref="Toolchain" />
/// <seealso cref="Flax.Build.Platforms.WindowsToolchainBase" />
public abstract class GDKToolchain : WindowsToolchainBase
{
/// <summary>
/// Initializes a new instance of the <see cref="GDKToolchain"/> class.
/// </summary>
/// <param name="platform">The platform.</param>
/// <param name="architecture">The architecture.</param>
protected GDKToolchain(GDKPlatform platform, TargetArchitecture architecture)
: base(platform, architecture, WindowsPlatformToolset.Latest, WindowsPlatformSDK.v10_0_19041_0)
{
// Setup system paths
SystemIncludePaths.Add(Path.Combine(GDK.Instance.RootPath, "GRDK\\GameKit\\Include"));
SystemLibraryPaths.Add(Path.Combine(GDK.Instance.RootPath, "GRDK\\GameKit\\Lib\\amd64"));
SystemLibraryPaths.Add(Path.Combine(GDK.Instance.RootPath, "GRDK\\ExtensionLibraries\\Xbox.Services.API.C\\DesignTime\\CommonConfiguration\\Neutral\\Lib\\Release\\" + Toolset));
}
/// <inheritdoc />
public override void SetupEnvironment(BuildOptions options)
{
base.SetupEnvironment(options);
options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_GDK");
options.CompileEnv.PreprocessorDefinitions.Add("WINAPI_FAMILY=WINAPI_FAMILY_GAMES");
options.CompileEnv.PreprocessorDefinitions.Add("_ATL_NO_DEFAULT_LIBS");
options.CompileEnv.PreprocessorDefinitions.Add("__WRL_NO_DEFAULT_LIB__");
options.LinkEnv.InputLibraries.Add("xgameruntime.lib");
options.LinkEnv.InputLibraries.Add("xgameplatform.lib");
options.LinkEnv.InputLibraries.Add("Microsoft.Xbox.Services.142.GDK.C.lib");
var toolsetPath = WindowsPlatformBase.GetToolsets()[Toolset];
var toolsPath = WindowsPlatformBase.GetVCToolPath64(Toolset);
if (options.CompileEnv.UseDebugCRT)
throw new Exception("Don't use debug CRT on GDK.");
var name = Path.GetFileName(toolsetPath);
var redistToolsPath = Path.Combine(toolsPath, "..", "..", "..", "..", "..", "..", "Redist/MSVC/");
var paths = Directory.GetDirectories(redistToolsPath, name.Substring(0, 5) + "*");
redistToolsPath = Path.Combine(paths[0], "x64", "Microsoft.VC" + (int)Toolset + ".CRT");
redistToolsPath = Utilities.RemovePathRelativeParts(redistToolsPath);
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "concrt140.dll"));
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "msvcp140.dll"));
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "msvcp140_1.dll"));
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "msvcp140_2.dll"));
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "msvcp140_codecvt_ids.dll"));
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vccorlib140.dll"));
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140.dll"));
options.DependencyFiles.Add(Path.Combine(redistToolsPath, "vcruntime140_1.dll"));
}
}
}