Files
UnrealEngineUWP/Engine/Source/Developer/DesktopPlatform/Private/Linux/DesktopPlatformLinux.cpp
Josh Adams cf651e0c63 Change 2898947 on 2016/03/08 by Mark.Satterthwaite
Move the Apple Driver Monitor stats into their own stat groups, DriverMonitor has the common stats, DriverMonitorAMD/Intel/Nvidia have the vendor/GPU specific stats. The Metal and OpenGL RHIs update the driver monitor stats group for the current GPU at the appropriate time.

Change 2898950 on 2016/03/08 by Mark.Satterthwaite

	More shader cache code documentation.

Change 2898952 on 2016/03/08 by Michael.Trepka

	Check GPU driver version and warn of bad drivers only on Windows

Change 2898964 on 2016/03/08 by Mark.Satterthwaite

	Only verify the vertex attribute layout for Metal in debug builds or when using development with the debug layer turned on. It reduces performance significantly and isn't all that helpful unless you are attempting to debug a mismatch.

Change 2898973 on 2016/03/08 by Mark.Satterthwaite

	Switch uniform buffers to managed memory on Mac as this is more appropriate for AMD & Nvidia GPUs.

Change 2898988 on 2016/03/08 by Mark.Satterthwaite

	Simplify MetalContext by having only one SubmitCommandsHint implementation.

Change 2899011 on 2016/03/08 by Mark.Satterthwaite

	Duplicate 4.11 CL #2898988:

	Proper fix for UE-25804 - we have to manually expand PF_G8 + SRGB to RGBA8_sRGB - this then fixes UE-27483.
	#jira UE-25804
	#jira UE-27483

Change 2899024 on 2016/03/08 by Mark.Satterthwaite

	Duplicate 4.11 CL #2887365 & CL #2887583:

	Allow InfiltratorDemoEditor under Metal to issue a query buffer reset without crashing - the function that switches to the new query buffer needs to reapply some of the draw-state so that future commands don't dereference nil.
	#jira UE-27513

	My earlier fix for UE-27513 overlooked various internal details that meant it wouldn't restore state correctly, would fail validation and could crash in a new place. This version will ensure that cached state is only reset when it is appropriate to do so and will restore it correct when doing a query buffer reset.
	#jira UE-27513

Change 2899418 on 2016/03/08 by Daniel.Lamb

	Added support for textboxes in the editor to convert uasset filenames into long package names. As this is more useful to the cooker and more portable for projects.
	#codereview Matt.Kuhlenschmidt
	#jira UE-27785

Change 2899419 on 2016/03/08 by Daniel.Lamb

	Added support for passing -opengl command through to launch on if the editor is started with it.
	#codereview Michael.Trepka

Change 2900846 on 2016/03/09 by Mark.Satterthwaite

	Reimplement Metal object lifetime tracking as stats in the stat-group, though the old system is maintained as a debug-only tool that could (and probably should) be extended to track over/under-release bugs. Currently the texture count will be distorted by texture SRVs so will need improvement but other stats should be reliable. In order to properly report the number of buffers the TResourcePool policy class must now define a FreeResource function, so I've added them to the appropriate places too.

Change 2900853 on 2016/03/09 by Mark.Satterthwaite

	Optimise away empty encoders that don't perform a clear operation on AMD & Intel, but not Nvidia or non-Mac Metal devices. This should slightly improve performance.

Change 2900927 on 2016/03/09 by Mark.Satterthwaite

	Implemented operation threshold submission of Metal command buffers to keep the GPU busier and not just idle waiting for the CPU. Whenever rhi.Metal.CommandBufferCommitThreshold is set to a value >0 and the current command buffer has >= draw/dispatch operations outstanding then the command-buffer will be committed at the next encoder boundary. The default value is 100 operations which is currently arbitrary and the feature can be disabled by setting the value to <= 0 in which case only explicit submissions will occur as previously.

Change 2901310 on 2016/03/09 by Mark.Satterthwaite

	Change OneColor clear shader setup so that it works with parallel encoding in Metal.

Change 2903002 on 2016/03/10 by Mark.Satterthwaite

	Instantiate the OneColor shaders once in Metal.

Change 2903274 on 2016/03/10 by Mark.Satterthwaite

	Remove more unnecessary parallel execution stalls from MetalRHI.

Change 2903402 on 2016/03/10 by Mark.Satterthwaite

	Implement Metal support for index buffer SRVs.

Change 2903419 on 2016/03/10 by Mark.Satterthwaite

	Always use Managed memory on Mac Metal for buffers.

Change 2905206 on 2016/03/11 by Mark.Satterthwaite

	Worked around UE-27818 "ElementalDemo Causes Invalid Rendering on AMD GPUs" - recent changes to allow mesh particles to write to velocity leave a texture-buffer unbound & then use a uniform value & an if-branch to guard against access but AMD's Mac GL driver notices that the buffer is referenced in the shader but not bound & promptly tries to fallback to Apple's S/W renderer regardless of what the uniform value is. That's legal behaviour for an OpenGL implementation so the C++ code has been changed to allocate and write the current transforms into the buffer for OpenGL when they wouldn't otherwise be provided. This is sufficient to avoid the problem without affecting any other API.

Change 2906217 on 2016/03/11 by Nick.Shin

	re-enabled http network file server
	it was disabled in CL: #2790193

	#jira UE-22166 HTML5 Cook on the fly will launch and then close browser

Change 2908203 on 2016/03/14 by Michael.Trepka

	Merging //UE4/Dev-Main to Dev-Platform (//UE4/Dev-Platform). Everything but SSF lib.

Change 2908553 on 2016/03/14 by Mark.Satterthwaite

	Force a submit & wait in Metal when contexts are being destroyed to prevent kernel panics in drivers which continue to process the now abandoned command-queue and encounter invalid resources (because we destroy them on shutdown).

Change 2908595 on 2016/03/14 by Michael.Trepka

	Fixed iOS compile error in MetalUniformBuffer.cpp

	#codereview Mark.Satterthwaite

Change 2910106 on 2016/03/15 by Mark.Satterthwaite

	Use a dispatch_semaphore not an FEvent for Metal free-list synchronisation as the dispatch_worker threads can't be properly setup for FStats and this causes problems.

Change 2910107 on 2016/03/15 by Mark.Satterthwaite

	Fix Metal reporting of GPU memory through the RHI as it is in bytes, not MB.

Change 2910138 on 2016/03/15 by Mark.Satterthwaite

	Properly retain/release dispatch_semaphore for Metal command buffer completion block & allow uniform buffer creation on parallel encoding thread.

Change 2911735 on 2016/03/16 by Nick.Shin

	housekeeping

	removing extra and inconsistant whitespace as well as making tabs & spaces consistant

[CL 2936662 by Josh Adams in Main branch]
2016-04-07 12:59:50 -04:00

463 lines
14 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "DesktopPlatformPrivatePCH.h"
#include "LinuxApplication.h"
#include "FeedbackContextMarkup.h"
//#include "LinuxNativeFeedbackContext.h"
// custom dialogs
#if WITH_LINUX_NATIVE_DIALOGS
#include "UNativeDialogs.h"
#else
#include "SlateFileDialogs.h"
#endif // WITH_LINUX_NATIVE_DIALOGS
#include "SDL.h"
#define LOCTEXT_NAMESPACE "DesktopPlatform"
#define MAX_FILETYPES_STR 4096
#define MAX_FILENAME_STR 65536
FDesktopPlatformLinux::FDesktopPlatformLinux()
: FDesktopPlatformBase()
{
#if WITH_LINUX_NATIVE_DIALOGS
bool bLNDInit = ULinuxNativeDialogs_Initialize();
if (bLNDInit)
{
UE_LOG(LogDesktopPlatform, Log, TEXT("LinuxNativeDialogs have been successfully initialized."));
}
else
{
UE_LOG(LogDesktopPlatform, Warning, TEXT("DesktopPlatformLinux could not initialize LinuxNativeDialogs - it will not work properly."));
}
#else
UE_LOG(LogDesktopPlatform, Log, TEXT("DesktopPlatformLinux is not using LinuxNativeDialogs."));
#endif // WITH_LINUX_NATIVE_DIALOGS
}
FDesktopPlatformLinux::~FDesktopPlatformLinux()
{
#if WITH_LINUX_NATIVE_DIALOGS
ULinuxNativeDialogs_Shutdown();
#endif
}
bool FDesktopPlatformLinux::OpenFileDialog(const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray<FString>& OutFilenames, int32& OutFilterIndex)
{
#if WITH_LINUX_NATIVE_DIALOGS
return FileDialogShared(false, ParentWindowHandle, DialogTitle, DefaultPath, DefaultFile, FileTypes, Flags, OutFilenames, OutFilterIndex);
#else
if (!FModuleManager::Get().IsModuleLoaded("SlateFileDialogs"))
{
FModuleManager::Get().LoadModule("SlateFileDialogs");
}
ISlateFileDialogsModule *FileDialog = FModuleManager::GetModulePtr<ISlateFileDialogsModule>("SlateFileDialogs");
if (FileDialog)
{
return FileDialog->OpenFileDialog(ParentWindowHandle, DialogTitle, DefaultPath, DefaultFile, FileTypes, Flags, OutFilenames, OutFilterIndex);
}
return false;
#endif // WITH_LINUX_NATIVE_DIALOGS
}
bool FDesktopPlatformLinux::OpenFileDialog(const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray<FString>& OutFilenames)
{
#if WITH_LINUX_NATIVE_DIALOGS
int32 DummyFilterIndex;
return FileDialogShared(false, ParentWindowHandle, DialogTitle, DefaultPath, DefaultFile, FileTypes, Flags, OutFilenames, DummyFilterIndex);
#else
if (!FModuleManager::Get().IsModuleLoaded("SlateFileDialogs"))
{
FModuleManager::Get().LoadModule("SlateFileDialogs");
}
ISlateFileDialogsModule *FileDialog = FModuleManager::GetModulePtr<ISlateFileDialogsModule>("SlateFileDialogs");
if (FileDialog)
{
return FileDialog->OpenFileDialog(ParentWindowHandle, DialogTitle, DefaultPath, DefaultFile, FileTypes, Flags, OutFilenames);
}
return false;
#endif // WITH_LINUX_NATIVE_DIALOGS
}
bool FDesktopPlatformLinux::SaveFileDialog(const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray<FString>& OutFilenames)
{
#if WITH_LINUX_NATIVE_DIALOGS
int32 DummyFilterIndex = 0;
return FileDialogShared(true, ParentWindowHandle, DialogTitle, DefaultPath, DefaultFile, FileTypes, Flags, OutFilenames, DummyFilterIndex);
#else
if (!FModuleManager::Get().IsModuleLoaded("SlateFileDialogs"))
{
FModuleManager::Get().LoadModule("SlateFileDialogs");
}
ISlateFileDialogsModule *FileDialog = FModuleManager::GetModulePtr<ISlateFileDialogsModule>("SlateFileDialogs");
if (FileDialog)
{
return FileDialog->SaveFileDialog(ParentWindowHandle, DialogTitle, DefaultPath, DefaultFile, FileTypes, Flags, OutFilenames);
}
return false;
#endif // WITH_LINUX_NATIVE_DIALOGS
}
bool FDesktopPlatformLinux::OpenDirectoryDialog(const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, FString& OutFolderName)
{
#if WITH_LINUX_NATIVE_DIALOGS
bool bSuccess;
struct UFileDialogHints hints = DEFAULT_UFILEDIALOGHINTS;
LinuxApplication->SetCapture(NULL);
hints.Action = UFileDialogActionOpenDirectory;
hints.WindowTitle = TCHAR_TO_UTF8(*DialogTitle);
hints.InitialDirectory = TCHAR_TO_UTF8(*DefaultPath);
UFileDialog* dialog = UFileDialog_Create(&hints);
while(UFileDialog_ProcessEvents(dialog))
{
FPlatformMisc::PumpMessages(true); // pretend that we're the main loop
}
const UFileDialogResult* result = UFileDialog_Result(dialog);
if (result)
{
if (result->count == 1)
{
OutFolderName = UTF8_TO_TCHAR(result->selection[0]);
//OutFolderName = IFileManager::Get().ConvertToRelativePath(*OutFolderName); // @todo (amigo): converting to relative path ends up without ../...
FPaths::NormalizeFilename(OutFolderName);
bSuccess = true;
}
else
{
bSuccess = false;
}
// Todo like in Windows, normalize files here instead of above
}
else
{
bSuccess = false;
}
UFileDialog_Destroy(dialog);
return bSuccess;
#else
if (!FModuleManager::Get().IsModuleLoaded("SlateFileDialogs"))
{
FModuleManager::Get().LoadModule("SlateFileDialogs");
}
ISlateFileDialogsModule *FileDialog = FModuleManager::GetModulePtr<ISlateFileDialogsModule>("SlateFileDialogs");
if (FileDialog)
{
return FileDialog->OpenDirectoryDialog(ParentWindowHandle, DialogTitle, DefaultPath, OutFolderName);
}
return false;
#endif // WITH_LINUX_NATIVE_DIALOGS
}
bool FDesktopPlatformLinux::OpenFontDialog(const void* ParentWindowHandle, FString& OutFontName, float& OutHeight, EFontImportFlags& OutFlags)
{
unimplemented();
return false;
}
bool FDesktopPlatformLinux::CanOpenLauncher(bool Install)
{
// TODO: no launcher support at the moment
return false;
}
bool FDesktopPlatformLinux::OpenLauncher(const FOpenLauncherOptions& Options)
{
// TODO: support launcher for realz
return true;
}
bool FDesktopPlatformLinux::FileDialogShared(bool bSave, const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray<FString>& OutFilenames, int32& OutFilterIndex)
{
#if WITH_LINUX_NATIVE_DIALOGS
FString Filename;
bool bSuccess;
LinuxApplication->SetCapture(NULL);
UE_LOG(LogDesktopPlatform, Warning, TEXT("FileDialogShared DialogTitle: %s, DefaultPath: %s, DefaultFile: %s, FileTypes: %s, Flags: %d"),
*DialogTitle, *DefaultPath, *DefaultFile, *FileTypes, Flags);
struct UFileDialogHints hints = DEFAULT_UFILEDIALOGHINTS;
hints.WindowTitle = TCHAR_TO_UTF8(*DialogTitle);
FString AllExtensionsSpaceDelim; // a string like "*.cpp *.h *.c"
// The strings will come in pairs, formatted as follows: Pair1String1|Pair1String2|Pair2String1|Pair2String2
// where the second string in the pair is the extension(s)
TArray<FString> Filters;
FileTypes.ParseIntoArray(Filters, TEXT("|"), true);
for( int32 ExtensionIndex = 1; ExtensionIndex < Filters.Num(); ExtensionIndex += 2)
{
const FString& Extensions = Filters[ExtensionIndex];
TArray<FString> ExtensionsArray;
// Extension can be either *.jpg or *.jpg;*.png -> in the latter case split at ';'
int32 UnusedIndex;
if (Extensions.FindChar(TEXT(';'), UnusedIndex))
{
Extensions.ParseIntoArray(ExtensionsArray, TEXT(";"), true);
}
else
{
ExtensionsArray.Add(Extensions); // just a single extension
}
for (const FString& Extension : ExtensionsArray)
{
if (AllExtensionsSpaceDelim.Find(Extension, ESearchCase::IgnoreCase) == INDEX_NONE)
{
AllExtensionsSpaceDelim += Extension;
AllExtensionsSpaceDelim += TEXT(" ");
}
}
}
FString AllExtensionsLumpedTogether(TEXT("All applicable ("));
AllExtensionsLumpedTogether += AllExtensionsSpaceDelim;
AllExtensionsLumpedTogether += TEXT(")");
char FileTypesBuf[MAX_FILETYPES_STR * 2] = {0,};
FTCHARToUTF8_Convert::Convert(FileTypesBuf, sizeof(FileTypesBuf), *AllExtensionsLumpedTogether, AllExtensionsLumpedTogether.Len());
hints.NameFilter = FileTypesBuf;
char DefPathBuf[MAX_FILENAME_STR * 2] = {0,};
FTCHARToUTF8_Convert::Convert(DefPathBuf, sizeof(DefPathBuf), *DefaultPath, DefaultPath.Len());
hints.InitialDirectory = DefPathBuf;
char DefFileBuf[MAX_FILENAME_STR * 2] = {0,};
FTCHARToUTF8_Convert::Convert(DefFileBuf, sizeof(DefFileBuf), *DefaultFile, DefaultFile.Len());
hints.DefaultFile = DefFileBuf;
if (bSave)
{
hints.Action = UFileDialogActionSave;
}
else
{
hints.Action = UFileDialogActionOpen;
}
UFileDialog* dialog = UFileDialog_Create(&hints);
while(UFileDialog_ProcessEvents(dialog))
{
FPlatformMisc::PumpMessages(true); // pretend that we're the main loop
}
const UFileDialogResult* result = UFileDialog_Result(dialog);
if (result)
{
if (result->count > 1)
{
// Todo better handling of multi-selects
UE_LOG(LogDesktopPlatform, Warning, TEXT("FileDialogShared Selected Files: %d"), result->count);
for(int i = 0;i < result->count;++i) {
//Filename = FUTF8ToTCHAR(result->selection[i], MAX_FILENAME_STR).Get();
Filename = UTF8_TO_TCHAR(result->selection[i]);
//new(OutFilenames) FString(Filename);
OutFilenames.Add(Filename);
Filename = IFileManager::Get().ConvertToRelativePath(*Filename);
FPaths::NormalizeFilename(Filename);
//UE_LOG(LogDesktopPlatform, Warning, TEXT("FileDialogShared File: %s"), *Filename);
}
bSuccess = true;
}
else if (result->count == 1)
{
//Filename = FUTF8ToTCHAR(result->selection[0], MAX_FILENAME_STR).Get();
Filename = UTF8_TO_TCHAR(result->selection[0]);
//new(OutFilenames) FString(Filename);
OutFilenames.Add(Filename);
Filename = IFileManager::Get().ConvertToRelativePath(*Filename);
FPaths::NormalizeFilename(Filename);
//UE_LOG(LogDesktopPlatform, Warning, TEXT("FileDialogShared File: %s"), *Filename);
bSuccess = true;
}
else
{
bSuccess = false;
}
// Todo like in Windows, normalize files here instead of above
}
else
{
bSuccess = false;
}
UFileDialog_Destroy(dialog);
return bSuccess;
#else
return false;
#endif // WITH_LINUX_NATIVE_DIALOGS
}
bool FDesktopPlatformLinux::RegisterEngineInstallation(const FString &RootDir, FString &OutIdentifier)
{
bool bRes = false;
if (IsValidRootDirectory(RootDir))
{
FConfigFile ConfigFile;
FString ConfigPath = FString(FPlatformProcess::ApplicationSettingsDir()) / FString(TEXT("UnrealEngine")) / FString(TEXT("Install.ini"));
ConfigFile.Read(ConfigPath);
FConfigSection &Section = ConfigFile.FindOrAdd(TEXT("Installations"));
OutIdentifier = FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens);
Section.AddUnique(*OutIdentifier, RootDir);
ConfigFile.Dirty = true;
ConfigFile.Write(ConfigPath);
}
return bRes;
}
void FDesktopPlatformLinux::EnumerateEngineInstallations(TMap<FString, FString> &OutInstallations)
{
EnumerateLauncherEngineInstallations(OutInstallations);
FString UProjectPath = FString(FPlatformProcess::ApplicationSettingsDir()) / "Unreal.uproject";
FArchive* File = IFileManager::Get().CreateFileWriter(*UProjectPath, FILEWRITE_EvenIfReadOnly);
if (File)
{
File->Close();
delete File;
}
else
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to write to Settings Directory", TCHAR_TO_UTF8(*UProjectPath), NULL);
}
FConfigFile ConfigFile;
FString ConfigPath = FString(FPlatformProcess::ApplicationSettingsDir()) / FString(TEXT("UnrealEngine")) / FString(TEXT("Install.ini"));
ConfigFile.Read(ConfigPath);
FConfigSection &Section = ConfigFile.FindOrAdd(TEXT("Installations"));
// @todo: currently we can enumerate only this installation
FString EngineDir = FPaths::EngineDir();
FString EngineId;
const FName* Key = Section.FindKey(EngineDir);
if (Key)
{
EngineId = Key->ToString();
}
else
{
if (!OutInstallations.FindKey(EngineDir))
{
EngineId = FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens);
Section.AddUnique(*EngineId, EngineDir);
ConfigFile.Dirty = true;
}
}
if (!EngineId.IsEmpty() && !OutInstallations.Find(EngineId))
{
OutInstallations.Add(EngineId, EngineDir);
}
ConfigFile.Write(ConfigPath);
IFileManager::Get().Delete(*UProjectPath);
}
bool FDesktopPlatformLinux::IsSourceDistribution(const FString &RootDir)
{
// Check for the existence of a GenerateProjectFiles.sh file. This allows compatibility with the GitHub 4.0 release.
FString GenerateProjectFilesPath = RootDir / TEXT("GenerateProjectFiles.sh");
if (IFileManager::Get().FileSize(*GenerateProjectFilesPath) >= 0)
{
return true;
}
// Otherwise use the default test
return FDesktopPlatformBase::IsSourceDistribution(RootDir);
}
bool FDesktopPlatformLinux::VerifyFileAssociations()
{
STUBBED("FDesktopPlatformLinux::VerifyFileAssociationsg");
return true; // for now we are associated
}
bool FDesktopPlatformLinux::UpdateFileAssociations()
{
//unimplemented();
STUBBED("FDesktopPlatformLinux::UpdateFileAssociations");
return false;
}
bool FDesktopPlatformLinux::OpenProject(const FString &ProjectFileName)
{
// Get the project filename in a native format
FString PlatformProjectFileName = ProjectFileName;
FPaths::MakePlatformFilename(PlatformProjectFileName);
STUBBED("FDesktopPlatformLinux::OpenProject");
return false;
}
bool FDesktopPlatformLinux::RunUnrealBuildTool(const FText& Description, const FString& RootDir, const FString& Arguments, FFeedbackContext* Warn)
{
// Get the path to UBT
FString UnrealBuildToolPath = RootDir / TEXT("Engine/Binaries/DotNET/UnrealBuildTool.exe");
if(IFileManager::Get().FileSize(*UnrealBuildToolPath) < 0)
{
Warn->Logf(ELogVerbosity::Error, TEXT("Couldn't find UnrealBuildTool at '%s'"), *UnrealBuildToolPath);
return false;
}
// Write the output
Warn->Logf(TEXT("Running %s %s"), *UnrealBuildToolPath, *Arguments);
// launch UBT with Mono
FString CmdLineParams = FString::Printf(TEXT("\"%s\" %s"), *UnrealBuildToolPath, *Arguments);
// Spawn it
int32 ExitCode = 0;
return FFeedbackContextMarkup::PipeProcessOutput(Description, TEXT("/usr/bin/mono"), CmdLineParams, Warn, &ExitCode) && ExitCode == 0;
}
bool FDesktopPlatformLinux::IsUnrealBuildToolRunning()
{
// For now assume that if mono application is running, we're running UBT
// @todo: we need to get the commandline for the mono process and check if UBT.exe is in there.
return FPlatformProcess::IsApplicationRunning(TEXT("mono"));
}
FFeedbackContext* FDesktopPlatformLinux::GetNativeFeedbackContext()
{
//unimplemented();
STUBBED("FDesktopPlatformLinux::GetNativeFeedbackContext");
return GWarn;
}
FString FDesktopPlatformLinux::GetUserTempPath()
{
return FString(FPlatformProcess::UserTempDir());
}
#undef LOCTEXT_NAMESPACE