1 #include "pch.h"
2 #include "PPSSPP_UWPMain.h"
3 
4 #include <mutex>
5 
6 #include "Common/File/FileUtil.h"
7 #include "Common/Net/HTTPClient.h"
8 #include "Common/Net/Resolve.h"
9 #include "Common/GPU/thin3d_create.h"
10 
11 #include "Common/Common.h"
12 #include "Common/Input/InputState.h"
13 #include "Common/File/VFS/VFS.h"
14 #include "Common/File/VFS/AssetReader.h"
15 #include "Common/Thread/ThreadUtil.h"
16 #include "Common/Data/Encoding/Utf8.h"
17 #include "Common/DirectXHelper.h"
18 #include "Common/File/FileUtil.h"
19 #include "Common/Log.h"
20 #include "Common/LogManager.h"
21 #include "Common/TimeUtil.h"
22 #include "Common/StringUtils.h"
23 #include "Common/System/Display.h"
24 #include "Common/System/NativeApp.h"
25 #include "Common/System/System.h"
26 
27 #include "Core/System.h"
28 #include "Core/Loaders.h"
29 #include "Core/Config.h"
30 
31 #include "NKCodeFromWindowsSystem.h"
32 #include "XAudioSoundStream.h"
33 #include "UWPHost.h"
34 #include "UWPUtil.h"
35 #include "StorageFileLoader.h"
36 #include "App.h"
37 
38 using namespace UWP;
39 using namespace Windows::Foundation;
40 using namespace Windows::Storage;
41 using namespace Windows::Storage::Streams;
42 using namespace Windows::System::Threading;
43 using namespace Windows::ApplicationModel::DataTransfer;
44 using namespace Windows::Devices::Enumeration;
45 using namespace Concurrency;
46 
47 // UGLY!
48 PPSSPP_UWPMain *g_main;
49 extern WindowsAudioBackend *winAudioBackend;
50 std::string langRegion;
51 // TODO: Use Microsoft::WRL::ComPtr<> for D3D11 objects?
52 // TODO: See https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/WindowsAudioSession for WASAPI with UWP
53 // TODO: Low latency input: https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/LowLatencyInput/cpp
54 
55 // Loads and initializes application assets when the application is loaded.
56 PPSSPP_UWPMain::PPSSPP_UWPMain(App ^app, const std::shared_ptr<DX::DeviceResources>& deviceResources) :
57 	app_(app),
58 	m_deviceResources(deviceResources)
59 {
60 	g_main = this;
61 
62 	net::Init();
63 
64 	host = new UWPHost();
65 	// Register to be notified if the Device is lost or recreated
66 	m_deviceResources->RegisterDeviceNotify(this);
67 
68 	// create_task(KnownFolders::GetFolderForUserAsync(nullptr, KnownFolderId::RemovableDevices)).then([this](StorageFolder ^));
69 
70 	// TODO: Change the timer settings if you want something other than the default variable timestep mode.
71 	// e.g. for 60 FPS fixed timestep update logic, call:
72 	/*
73 	m_timer.SetFixedTimeStep(true);
74 	m_timer.SetTargetElapsedSeconds(1.0 / 60);
75 	*/
76 
77 	ctx_.reset(new UWPGraphicsContext(deviceResources));
78 
79 	const Path &exePath = File::GetExeDirectory();
80 	VFSRegister("", new DirectoryAssetReader(exePath / "Content"));
81 	VFSRegister("", new DirectoryAssetReader(exePath));
82 
83 	wchar_t lcCountry[256];
84 
85 	if (0 != GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, lcCountry, 256)) {
86 		langRegion = ConvertWStringToUTF8(lcCountry);
87 		for (size_t i = 0; i < langRegion.size(); i++) {
88 			if (langRegion[i] == '-')
89 				langRegion[i] = '_';
90 		}
91 	} else {
92 		langRegion = "en_US";
93 	}
94 
95 	std::wstring memstickFolderW = ApplicationData::Current->LocalFolder->Path->Data();
96 	g_Config.memStickDirectory = Path(memstickFolderW);
97 
98 	// On Win32 it makes more sense to initialize the system directories here
99 	// because the next place it was called was in the EmuThread, and it's too late by then.
100 	InitSysDirectories();
101 
102 	LogManager::Init(&g_Config.bEnableLogging);
103 
104 	// Load config up here, because those changes below would be overwritten
105 	// if it's not loaded here first.
106 	g_Config.SetSearchPath(GetSysDirectory(DIRECTORY_SYSTEM));
107 	g_Config.Load();
108 
109 	bool debugLogLevel = false;
110 
111 	g_Config.iGPUBackend = (int)GPUBackend::DIRECT3D11;
112 
113 	if (debugLogLevel) {
114 		LogManager::GetInstance()->SetAllLogLevels(LogTypes::LDEBUG);
115 	}
116 
117 	const char *argv[2] = { "fake", nullptr };
118 
119 
120 	std::string cacheFolder = ConvertWStringToUTF8(ApplicationData::Current->LocalFolder->Path->Data());
121 
122 	NativeInit(1, argv, "", "", cacheFolder.c_str());
123 
124 	NativeInitGraphics(ctx_.get());
125 	NativeResized();
126 
127 	int width = m_deviceResources->GetScreenViewport().Width;
128 	int height = m_deviceResources->GetScreenViewport().Height;
129 
130 	ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, m_deviceResources->GetBackBufferRenderTargetView());
131 	InputDevice::BeginPolling();
132 }
133 
~PPSSPP_UWPMain()134 PPSSPP_UWPMain::~PPSSPP_UWPMain() {
135 	InputDevice::StopPolling();
136 	ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_BACKBUFFER, 0, 0, nullptr);
137 	NativeShutdownGraphics();
138 	NativeShutdown();
139 
140 	// Deregister device notification
141 	m_deviceResources->RegisterDeviceNotify(nullptr);
142 	net::Shutdown();
143 }
144 
145 // Updates application state when the window size changes (e.g. device orientation change)
CreateWindowSizeDependentResources()146 void PPSSPP_UWPMain::CreateWindowSizeDependentResources() {
147 	ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_BACKBUFFER, 0, 0, nullptr);
148 
149 	NativeResized();
150 
151 	int width = m_deviceResources->GetScreenViewport().Width;
152 	int height = m_deviceResources->GetScreenViewport().Height;
153 	ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_BACKBUFFER, width, height, m_deviceResources->GetBackBufferRenderTargetView());
154 }
155 
156 // Renders the current frame according to the current application state.
157 // Returns true if the frame was rendered and is ready to be displayed.
Render()158 bool PPSSPP_UWPMain::Render() {
159 	ctx_->GetDrawContext()->HandleEvent(Draw::Event::PRESENTED, 0, 0, nullptr, nullptr);
160 	NativeUpdate();
161 
162 	static bool hasSetThreadName = false;
163 	if (!hasSetThreadName) {
164 		SetCurrentThreadName("UWPRenderThread");
165 		hasSetThreadName = true;
166 	}
167 
168 	auto context = m_deviceResources->GetD3DDeviceContext();
169 
170 	switch (m_deviceResources->ComputeDisplayRotation()) {
171 	case DXGI_MODE_ROTATION_IDENTITY: g_display_rotation = DisplayRotation::ROTATE_0; break;
172 	case DXGI_MODE_ROTATION_ROTATE90: g_display_rotation = DisplayRotation::ROTATE_90; break;
173 	case DXGI_MODE_ROTATION_ROTATE180: g_display_rotation = DisplayRotation::ROTATE_180; break;
174 	case DXGI_MODE_ROTATION_ROTATE270: g_display_rotation = DisplayRotation::ROTATE_270; break;
175 	}
176 	// Not super elegant but hey.
177 	memcpy(&g_display_rot_matrix, &m_deviceResources->GetOrientationTransform3D(), sizeof(float) * 16);
178 
179 	// Reset the viewport to target the whole screen.
180 	auto viewport = m_deviceResources->GetScreenViewport();
181 
182 	pixel_xres = viewport.Width;
183 	pixel_yres = viewport.Height;
184 
185 	if (g_display_rotation == DisplayRotation::ROTATE_90 || g_display_rotation == DisplayRotation::ROTATE_270) {
186 		// We need to swap our width/height.
187 		std::swap(pixel_xres, pixel_yres);
188 	}
189 
190 	g_dpi = m_deviceResources->GetActualDpi();
191 
192 	if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_MOBILE) {
193 		// Boost DPI a bit to look better.
194 		g_dpi *= 96.0f / 136.0f;
195 	}
196 	g_dpi_scale_x = 96.0f / g_dpi;
197 	g_dpi_scale_y = 96.0f / g_dpi;
198 
199 	pixel_in_dps_x = 1.0f / g_dpi_scale_x;
200 	pixel_in_dps_y = 1.0f / g_dpi_scale_y;
201 
202 	dp_xres = pixel_xres * g_dpi_scale_x;
203 	dp_yres = pixel_yres * g_dpi_scale_y;
204 
205 	context->RSSetViewports(1, &viewport);
206 
207 	NativeRender(ctx_.get());
208 	return true;
209 }
210 
211 // Notifies renderers that device resources need to be released.
OnDeviceLost()212 void PPSSPP_UWPMain::OnDeviceLost() {
213 	ctx_->GetDrawContext()->HandleEvent(Draw::Event::LOST_DEVICE, 0, 0, nullptr);
214 }
215 
216 // Notifies renderers that device resources may now be recreated.
OnDeviceRestored()217 void PPSSPP_UWPMain::OnDeviceRestored() {
218 	CreateWindowSizeDependentResources();
219 
220 	ctx_->GetDrawContext()->HandleEvent(Draw::Event::GOT_DEVICE, 0, 0, nullptr);
221 }
222 
OnKeyDown(int scanCode,Windows::System::VirtualKey virtualKey,int repeatCount)223 void PPSSPP_UWPMain::OnKeyDown(int scanCode, Windows::System::VirtualKey virtualKey, int repeatCount) {
224 	auto iter = virtualKeyCodeToNKCode.find(virtualKey);
225 	if (iter != virtualKeyCodeToNKCode.end()) {
226 		KeyInput key{};
227 		key.deviceId = DEVICE_ID_KEYBOARD;
228 		key.keyCode = iter->second;
229 		key.flags = KEY_DOWN | (repeatCount > 1 ? KEY_IS_REPEAT : 0);
230 		NativeKey(key);
231 	}
232 }
233 
OnKeyUp(int scanCode,Windows::System::VirtualKey virtualKey)234 void PPSSPP_UWPMain::OnKeyUp(int scanCode, Windows::System::VirtualKey virtualKey) {
235 	auto iter = virtualKeyCodeToNKCode.find(virtualKey);
236 	if (iter != virtualKeyCodeToNKCode.end()) {
237 		KeyInput key{};
238 		key.deviceId = DEVICE_ID_KEYBOARD;
239 		key.keyCode = iter->second;
240 		key.flags = KEY_UP;
241 		NativeKey(key);
242 	}
243 }
244 
OnMouseWheel(float delta)245 void PPSSPP_UWPMain::OnMouseWheel(float delta) {
246 	int key = NKCODE_EXT_MOUSEWHEEL_UP;
247 	if (delta < 0) {
248 		key = NKCODE_EXT_MOUSEWHEEL_DOWN;
249 	} else if (delta == 0) {
250 		return;
251 	}
252 
253 	KeyInput keyInput{};
254 	keyInput.keyCode = key;
255 	keyInput.deviceId = DEVICE_ID_MOUSE;
256 	keyInput.flags = KEY_DOWN | KEY_UP;
257 	NativeKey(keyInput);
258 }
259 
OnHardwareButton(HardwareButton button)260 bool PPSSPP_UWPMain::OnHardwareButton(HardwareButton button) {
261 	KeyInput keyInput{};
262 	keyInput.deviceId = DEVICE_ID_KEYBOARD;
263 	keyInput.flags = KEY_DOWN | KEY_UP;
264 	switch (button) {
265 	case HardwareButton::BACK:
266 		keyInput.keyCode = NKCODE_BACK;
267 		return NativeKey(keyInput);
268 	default:
269 		return false;
270 	}
271 }
272 
OnTouchEvent(int touchEvent,int touchId,float x,float y,double timestamp)273 void PPSSPP_UWPMain::OnTouchEvent(int touchEvent, int touchId, float x, float y, double timestamp) {
274 	// We get the coordinate in Windows' device independent pixels already. So let's undo that,
275 	// and then apply our own "dpi".
276 	float dpiFactor_x = m_deviceResources->GetActualDpi() / 96.0f;
277 	float dpiFactor_y = dpiFactor_x;
278 	dpiFactor_x /= pixel_in_dps_x;
279 	dpiFactor_y /= pixel_in_dps_y;
280 
281 	TouchInput input{};
282 	input.id = touchId;
283 	input.x = x * dpiFactor_x;
284 	input.y = y * dpiFactor_y;
285 	input.flags = touchEvent;
286 	input.timestamp = timestamp;
287 	NativeTouch(input);
288 
289 	KeyInput key{};
290 	key.deviceId = DEVICE_ID_MOUSE;
291 	if (touchEvent & TOUCH_DOWN) {
292 		key.keyCode = NKCODE_EXT_MOUSEBUTTON_1;
293 		key.flags = KEY_DOWN;
294 		NativeKey(key);
295 	}
296 	if (touchEvent & TOUCH_UP) {
297 		key.keyCode = NKCODE_EXT_MOUSEBUTTON_1;
298 		key.flags = KEY_UP;
299 		NativeKey(key);
300 	}
301 }
302 
OnSuspend()303 void PPSSPP_UWPMain::OnSuspend() {
304 	// TODO
305 }
306 
307 void PPSSPP_UWPMain::LoadStorageFile(StorageFile ^file) {
308 	std::unique_ptr<FileLoaderFactory> factory(new StorageFileLoaderFactory(file, IdentifiedFileType::PSP_ISO));
309 	RegisterFileLoaderFactory("override://", std::move(factory));
310 	NativeMessageReceived("boot", "override://file");
311 }
312 
UWPGraphicsContext(std::shared_ptr<DX::DeviceResources> resources)313 UWPGraphicsContext::UWPGraphicsContext(std::shared_ptr<DX::DeviceResources> resources) {
314 	std::vector<std::string> adapterNames;
315 
316 	draw_ = Draw::T3DCreateD3D11Context(
317 		resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetD3DDevice(), resources->GetD3DDeviceContext(), resources->GetDeviceFeatureLevel(), 0, adapterNames);
318 	bool success = draw_->CreatePresets();
319 	_assert_(success);
320 }
321 
Shutdown()322 void UWPGraphicsContext::Shutdown() {
323 	delete draw_;
324 }
325 
SwapInterval(int interval)326 void UWPGraphicsContext::SwapInterval(int interval) {
327 
328 }
329 
System_GetProperty(SystemProperty prop)330 std::string System_GetProperty(SystemProperty prop) {
331 	static bool hasCheckedGPUDriverVersion = false;
332 	switch (prop) {
333 	case SYSPROP_NAME:
334 		return "Windows 10 Universal";
335 	case SYSPROP_LANGREGION:
336 		return langRegion;
337 	case SYSPROP_CLIPBOARD_TEXT:
338 		/* TODO: Need to either change this API or do this on a thread in an ugly fashion.
339 		DataPackageView ^view = Clipboard::GetContent();
340 		if (view) {
341 			string text = await view->GetTextAsync();
342 		}
343 		*/
344 		return "";
345 	case SYSPROP_GPUDRIVER_VERSION:
346 		return "";
347 	default:
348 		return "";
349 	}
350 }
351 
System_GetPropertyStringVec(SystemProperty prop)352 std::vector<std::string> System_GetPropertyStringVec(SystemProperty prop) {
353 	std::vector<std::string> result;
354 	switch (prop) {
355 	case SYSPROP_TEMP_DIRS:
356 	{
357 		std::wstring tempPath(MAX_PATH, '\0');
358 		size_t sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
359 		if (sz >= tempPath.size()) {
360 			tempPath.resize(sz);
361 			sz = GetTempPath((DWORD)tempPath.size(), &tempPath[0]);
362 		}
363 		// Need to resize off the null terminator either way.
364 		tempPath.resize(sz);
365 		result.push_back(ConvertWStringToUTF8(tempPath));
366 
367 		if (getenv("TMPDIR") && strlen(getenv("TMPDIR")) != 0)
368 			result.push_back(getenv("TMPDIR"));
369 		if (getenv("TMP") && strlen(getenv("TMP")) != 0)
370 			result.push_back(getenv("TMP"));
371 		if (getenv("TEMP") && strlen(getenv("TEMP")) != 0)
372 			result.push_back(getenv("TEMP"));
373 		return result;
374 	}
375 
376 	default:
377 		return result;
378 	}
379 }
380 
System_GetPropertyInt(SystemProperty prop)381 int System_GetPropertyInt(SystemProperty prop) {
382 	switch (prop) {
383 	case SYSPROP_AUDIO_SAMPLE_RATE:
384 		return winAudioBackend ? winAudioBackend->GetSampleRate() : -1;
385 	case SYSPROP_DEVICE_TYPE:
386 	{
387 		auto ver = Windows::System::Profile::AnalyticsInfo::VersionInfo;
388 		if (ver->DeviceFamily == "Windows.Mobile") {
389 			return DEVICE_TYPE_MOBILE;
390 		} else if (ver->DeviceFamily == "Windows.Xbox") {
391 			return DEVICE_TYPE_TV;
392 		} else {
393 			return DEVICE_TYPE_DESKTOP;
394 		}
395 	}
396 	default:
397 		return -1;
398 	}
399 }
400 
System_GetPropertyFloat(SystemProperty prop)401 float System_GetPropertyFloat(SystemProperty prop) {
402 	switch (prop) {
403 	case SYSPROP_DISPLAY_REFRESH_RATE:
404 		return 60.f;
405 	case SYSPROP_DISPLAY_SAFE_INSET_LEFT:
406 	case SYSPROP_DISPLAY_SAFE_INSET_RIGHT:
407 	case SYSPROP_DISPLAY_SAFE_INSET_TOP:
408 	case SYSPROP_DISPLAY_SAFE_INSET_BOTTOM:
409 		return 0.0f;
410 	default:
411 		return -1;
412 	}
413 }
414 
VulkanMayBeAvailable()415 bool VulkanMayBeAvailable() {
416 	return false;
417 }
418 
System_GetPropertyBool(SystemProperty prop)419 bool System_GetPropertyBool(SystemProperty prop) {
420 	switch (prop) {
421 	case SYSPROP_HAS_FILE_BROWSER:
422 		return true;
423 	case SYSPROP_HAS_FOLDER_BROWSER:
424 		return false;  // at least I don't know a usable one
425 	case SYSPROP_HAS_IMAGE_BROWSER:
426 		return false;
427 	case SYSPROP_HAS_BACK_BUTTON:
428 		return true;
429 	case SYSPROP_APP_GOLD:
430 #ifdef GOLD
431 		return true;
432 #else
433 		return false;
434 #endif
435 	case SYSPROP_CAN_JIT:
436 		return true;
437 	default:
438 		return false;
439 	}
440 }
441 
System_SendMessage(const char * command,const char * parameter)442 void System_SendMessage(const char *command, const char *parameter) {
443 	using namespace concurrency;
444 
445 	if (!strcmp(command, "finish")) {
446 		// Not really supposed to support this under UWP.
447 	} else if (!strcmp(command, "browse_file")) {
448 		auto picker = ref new Windows::Storage::Pickers::FileOpenPicker();
449 		picker->ViewMode = Pickers::PickerViewMode::List;
450 
451 		// These are single files that can be loaded directly using StorageFileLoader.
452 		picker->FileTypeFilter->Append(".cso");
453 		picker->FileTypeFilter->Append(".iso");
454 
455 		// Can't load these this way currently, they require mounting the underlying folder.
456 		// picker->FileTypeFilter->Append(".bin");
457 		// picker->FileTypeFilter->Append(".elf");
458 		picker->SuggestedStartLocation = Pickers::PickerLocationId::DocumentsLibrary;
459 
460 		create_task(picker->PickSingleFileAsync()).then([](StorageFile ^file){
461 			if (file) {
462 				g_main->LoadStorageFile(file);
463 			}
464 		});
465 	} else if (!strcmp(command, "toggle_fullscreen")) {
466 		auto view = Windows::UI::ViewManagement::ApplicationView::GetForCurrentView();
467 		bool flag = !view->IsFullScreenMode;
468 		if (strcmp(parameter, "0") == 0) {
469 			flag = false;
470 		} else if (strcmp(parameter, "1") == 0){
471 			flag = true;
472 		}
473 		if (flag) {
474 			view->TryEnterFullScreenMode();
475 		} else {
476 			view->ExitFullScreenMode();
477 		}
478 	}
479 }
480 
OpenDirectory(const char * path)481 void OpenDirectory(const char *path) {
482 	// Unsupported
483 }
484 
LaunchBrowser(const char * url)485 void LaunchBrowser(const char *url) {
486 	auto uri = ref new Windows::Foundation::Uri(ToPlatformString(url));
487 
488 	create_task(Windows::System::Launcher::LaunchUriAsync(uri)).then([](bool b) {});
489 }
490 
Vibrate(int length_ms)491 void Vibrate(int length_ms) {
492 #if _M_ARM
493 	if (length_ms == -1 || length_ms == -3)
494 		length_ms = 50;
495 	else if (length_ms == -2)
496 		length_ms = 25;
497 	else
498 		return;
499 
500 	auto timeSpan = Windows::Foundation::TimeSpan();
501 	timeSpan.Duration = length_ms * 10000;
502 	// TODO: Can't use this?
503 	// Windows::Phone::Devices::Notification::VibrationDevice::GetDefault()->Vibrate(timeSpan);
504 #endif
505 }
506 
System_AskForPermission(SystemPermission permission)507 void System_AskForPermission(SystemPermission permission) {
508 	// Do nothing
509 }
510 
System_GetPermissionStatus(SystemPermission permission)511 PermissionStatus System_GetPermissionStatus(SystemPermission permission) {
512 	return PERMISSION_STATUS_GRANTED;
513 }
514 
System_InputBoxGetString(const std::string & title,const std::string & defaultValue,std::function<void (bool,const std::string &)> cb)515 void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function<void(bool, const std::string &)> cb) {
516 	// TODO
517 	cb(false, "");
518 }
519 
GetCPUBrandString()520 std::string GetCPUBrandString() {
521 	Platform::String^ cpu_id = nullptr;
522 	Platform::String^ cpu_name = nullptr;
523 
524 	// GUID_DEVICE_PROCESSOR: {97FADB10-4E33-40AE-359C-8BEF029DBDD0}
525 	Platform::String^ if_filter = L"System.Devices.InterfaceClassGuid:=\"{97FADB10-4E33-40AE-359C-8BEF029DBDD0}\"";
526 
527 	// Enumerate all CPU DeviceInterfaces, and get DeviceInstanceID of the first one.
528 	auto if_task = create_task(
529 		DeviceInformation::FindAllAsync(if_filter)).then([&](DeviceInformationCollection ^ collection) {
530 			if (collection->Size > 0) {
531 				auto cpu = collection->GetAt(0);
532 				auto id = cpu->Properties->Lookup(L"System.Devices.DeviceInstanceID");
533 				cpu_id = dynamic_cast<Platform::String^>(id);
534 			}
535 	});
536 
537 	try {
538 		if_task.wait();
539 	}
540 	catch (const std::exception & e) {
541 		const char* what = e.what();
542 		INFO_LOG(SYSTEM, "%s", what);
543 	}
544 
545 	if (cpu_id != nullptr) {
546 		// Get the Device with the same ID as the DeviceInterface
547 		// Then get the name (description) of that Device
548 		// We have to do this because the DeviceInterface we get doesn't have a proper description.
549 		Platform::String^ dev_filter = L"System.Devices.DeviceInstanceID:=\"" + cpu_id + L"\"";
550 
551 		auto dev_task = create_task(
552 			DeviceInformation::FindAllAsync(dev_filter, {}, DeviceInformationKind::Device)).then(
553 				[&](DeviceInformationCollection ^ collection) {
554 					if (collection->Size > 0) {
555 						cpu_name = collection->GetAt(0)->Name;
556 					}
557 		});
558 
559 		try {
560 			dev_task.wait();
561 		}
562 		catch (const std::exception & e) {
563 			const char* what = e.what();
564 			INFO_LOG(SYSTEM, "%s", what);
565 		}
566 	}
567 
568 	if (cpu_name != nullptr) {
569 		return FromPlatformString(cpu_name);
570 	} else {
571 		return "Unknown";
572 	}
573 }
574 
575 // Emulation of TlsAlloc for Windows 10. Used by glslang. Doesn't actually seem to work, other than fixing the linking errors?
576 
577 extern "C" {
__imp_TlsAlloc()578 DWORD WINAPI __imp_TlsAlloc() {
579 	return FlsAlloc(nullptr);
580 }
__imp_TlsFree(DWORD index)581 BOOL WINAPI __imp_TlsFree(DWORD index) {
582 	return FlsFree(index);
583 }
__imp_TlsSetValue(DWORD dwTlsIndex,LPVOID lpTlsValue)584 BOOL WINAPI __imp_TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) {
585 	return FlsSetValue(dwTlsIndex, lpTlsValue);
586 }
__imp_TlsGetValue(DWORD dwTlsIndex)587 LPVOID WINAPI __imp_TlsGetValue(DWORD dwTlsIndex) {
588 	return FlsGetValue(dwTlsIndex);
589 }
590 }
591