You're breathtaking!

This commit is contained in:
Wojtek Figat
2020-12-07 23:40:54 +01:00
commit 6fb9eee74c
5143 changed files with 1153594 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "CollectAssetsStep.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Asset.h"
#include "Engine/Content/AssetReference.h"
#include "Engine/Content/Assets/Texture.h"
#include "Engine/Core/Log.h"
#include "Engine/Content/Assets/CubeTexture.h"
#include "Engine/Content/Assets/Shader.h"
#include "Engine/Content/Cache/AssetsCache.h"
bool CollectAssetsStep::Process(CookingData& data, Asset* asset)
{
// Skip virtual/temporary assets
if (asset->IsVirtual())
return false;
// Keep reference to the asset
AssetReference<Asset> ref(asset);
// Asset should have loaded data
if (asset->WaitForLoaded())
return false;
// Gather asset references
_references.Clear();
asset->Locker.Lock();
asset->GetReferences(_references);
asset->Locker.Unlock();
_assetsQueue.Add(_references);
return false;
}
bool CollectAssetsStep::Perform(CookingData& data)
{
LOG(Info, "Searching for assets to include in a build. Using {0} root assets.", data.RootAssets.Count());
data.StepProgress(TEXT("Collecting assets"), 0);
// Initialize assets queue
_assetsQueue.Clear();
_assetsQueue.EnsureCapacity(1024);
for (auto i = data.RootAssets.Begin(); i.IsNotEnd(); ++i)
_assetsQueue.Add(i->Item);
// Iterate through the assets graph
AssetInfo assetInfo;
while (_assetsQueue.HasItems())
{
BUILD_STEP_CANCEL_CHECK;
const auto assetId = _assetsQueue.Dequeue();
// Skip already processed or invalid assets
if (!assetId.IsValid()
|| !Content::GetRegistry()->FindAsset(assetId, assetInfo)
|| data.Assets.Contains(assetId))
continue;
// Skip some assets (with no refs and not required to load)
if (assetInfo.TypeName == Texture::TypeName ||
assetInfo.TypeName == CubeTexture::TypeName ||
assetInfo.TypeName == Shader::TypeName)
{
LOG_STR(Info, assetInfo.Path);
data.Assets.Add(assetId);
continue;
}
// Load asset
const auto asset = Content::LoadAsync<Asset>(assetId);
if (asset == nullptr)
continue;
// Process that asset
LOG_STR(Info, asset->GetPath());
data.Assets.Add(assetId);
Process(data, asset);
}
data.Stats.TotalAssets = data.Assets.Count();
LOG(Info, "Found {0} assets to deploy!", data.Assets.Count());
return false;
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Editor/Cooker/GameCooker.h"
class Asset;
/// <summary>
/// Cooking step that uses the root assets collection to find all dependant assets to include in the build.
/// </summary>
/// <seealso cref="GameCooker::BuildStep" />
class CollectAssetsStep : public GameCooker::BuildStep
{
private:
Array<Guid> _assetsQueue;
Array<Guid> _references;
bool Process(CookingData& data, Asset* asset);
public:
// [BuildStep]
bool Perform(CookingData& data) override;
};

View File

@@ -0,0 +1,298 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "CompileScriptsStep.h"
#include "Editor/Scripting/ScriptsBuilder.h"
#include "Engine/Scripting/BinaryModule.h"
#include "Engine/Scripting/Scripting.h"
#include "Engine/Platform/FileSystem.h"
#include "Engine/Platform/File.h"
#include "Engine/Serialization/Json.h"
#include "Engine/Serialization/JsonTools.h"
#include "Engine/Serialization/JsonWriters.h"
#include "Editor/Cooker/PlatformTools.h"
#include "Editor/Editor.h"
bool CompileScriptsStep::DeployBinaries(CookingData& data, const String& path, const String& projectFolderPath)
{
if (_deployedBuilds.Contains(path))
return false;
LOG(Info, "Deploying binaries from build {0}", path);
_deployedBuilds.Add(path);
// Read file contents
Array<byte> fileData;
if (File::ReadAllBytes(path, fileData))
{
LOG(Error, "Failed to read file {0} contents.", path);
return true;
}
// Parse Json data
rapidjson_flax::Document document;
document.Parse((const char*)fileData.Get(), fileData.Count());
if (document.HasParseError())
{
LOG(Error, "Failed to parse {0} file contents.", path);
return true;
}
// Deploy all references
auto referencesMember = document.FindMember("References");
if (referencesMember != document.MemberEnd())
{
auto& referencesArray = referencesMember->value;
ASSERT(referencesArray.IsArray());
for (rapidjson::SizeType i = 0; i < referencesArray.Size(); i++)
{
auto& reference = referencesArray[i];
String referenceProjectPath = JsonTools::GetString(reference, "ProjectPath", String::Empty);
String referencePath = JsonTools::GetString(reference, "Path", String::Empty);
if (referenceProjectPath.IsEmpty() || referencePath.IsEmpty())
{
LOG(Error, "Empty reference in {0}.", path);
return true;
}
Scripting::ProcessBuildInfoPath(referenceProjectPath, projectFolderPath);
Scripting::ProcessBuildInfoPath(referencePath, projectFolderPath);
String referenceProjectFolderPath = StringUtils::GetDirectoryName(referenceProjectPath);
if (DeployBinaries(data, referencePath, referenceProjectFolderPath))
{
LOG(Error, "Failed to load reference in {0} to {1}.", path, referenceProjectPath);
return true;
}
}
}
// Deploy all binary modules
auto binaryModulesMember = document.FindMember("BinaryModules");
if (binaryModulesMember != document.MemberEnd())
{
auto& binaryModulesArray = binaryModulesMember->value;
ASSERT(binaryModulesArray.IsArray());
for (rapidjson::SizeType i = 0; i < binaryModulesArray.Size(); i++)
{
auto& binaryModule = binaryModulesArray[i];
auto& e = data.BinaryModules.AddOne();
const auto nameMember = binaryModule.FindMember("Name");
if (nameMember == binaryModule.MemberEnd())
{
LOG(Error, "Failed to process file {0}. Missing binary module name.", path);
return true;
}
e.Name = nameMember->value.GetText();
StringAnsi nameAnsi(nameMember->value.GetString(), nameMember->value.GetStringLength());
e.NativePath = JsonTools::GetString(binaryModule, "NativePath", String::Empty);
e.ManagedPath = JsonTools::GetString(binaryModule, "ManagedPath", String::Empty);
Scripting::ProcessBuildInfoPath(e.NativePath, projectFolderPath);
Scripting::ProcessBuildInfoPath(e.ManagedPath, projectFolderPath);
e.NativePath = StringUtils::GetFileName(e.NativePath);
e.ManagedPath = StringUtils::GetFileName(e.ManagedPath);
LOG(Info, "Collecting binary module {0}", e.Name);
}
}
// Deploy files
Array<String> files(16);
const String outputPath = StringUtils::GetDirectoryName(path);
FileSystem::DirectoryGetFiles(files, outputPath, TEXT("*.*"), DirectorySearchOption::TopDirectoryOnly);
for (int32 i = files.Count() - 1; i >= 0; i--)
{
bool skip = false;
const String& file = files[i];
for (auto& extension : _extensionsToSkip)
{
if (file.EndsWith(extension))
{
skip = true;
break;
}
}
if (skip)
files.RemoveAt(i);
}
for (auto& file : files)
{
const String dst = data.OutputPath / StringUtils::GetFileName(file);
if (dst != file && FileSystem::CopyFile(dst, file))
{
data.Error(TEXT("Failed to copy file from {0} to {1}."), file, dst);
return true;
}
}
return false;
}
bool CompileScriptsStep::Perform(CookingData& data)
{
data.StepProgress(TEXT("Compiling game scripts"), 0);
const ProjectInfo* project = Editor::Project;
const String& target = project->GameTarget;
if (target.IsEmpty())
{
LOG(Error, "Empty GameTarget in project.");
return true;
}
const Char *platform, *architecture, *configuration = ::ToString(data.Configuration);
switch (data.Platform)
{
case BuildPlatform::Windows32:
platform = TEXT("Windows");
architecture = TEXT("x86");
break;
case BuildPlatform::Windows64:
platform = TEXT("Windows");
architecture = TEXT("x64");
break;
case BuildPlatform::UWPx86:
platform = TEXT("UWP");
architecture = TEXT("x86");
break;
case BuildPlatform::UWPx64:
platform = TEXT("UWP");
architecture = TEXT("x64");
break;
case BuildPlatform::XboxOne:
platform = TEXT("XboxOne");
architecture = TEXT("x64");
break;
case BuildPlatform::LinuxX64:
platform = TEXT("Linux");
architecture = TEXT("x64");
break;
case BuildPlatform::PS4:
platform = TEXT("PS4");
architecture = TEXT("x64");
break;
case BuildPlatform::XboxScarlett:
platform = TEXT("XboxScarlett");
architecture = TEXT("x64");
break;
case BuildPlatform::AndroidARM64:
platform = TEXT("Android");
architecture = TEXT("ARM64");
break;
default:
return true;
}
_extensionsToSkip.Clear();
_extensionsToSkip.Add(TEXT(".exp"));
_extensionsToSkip.Add(TEXT(".ilk"));
_extensionsToSkip.Add(TEXT(".lib"));
_extensionsToSkip.Add(TEXT(".a"));
_extensionsToSkip.Add(TEXT(".Build.json"));
if (data.Configuration == BuildConfiguration::Release)
{
_extensionsToSkip.Add(TEXT(".xml"));
_extensionsToSkip.Add(TEXT(".pdb"));
}
_deployedBuilds.Clear();
data.BinaryModules.Clear();
if (data.Tools->OnScriptsCompilationStart(data))
return true;
BUILD_STEP_CANCEL_CHECK;
// Compile the scripts
LOG(Info, "Starting scripts compilation for game...");
const String logFile = data.CacheDirectory / TEXT("CompileLog.txt");
auto args = String::Format(
TEXT("-log -logfile=\"{4}\" -build -mutex -buildtargets={0} -platform={1} -arch={2} -configuration={3}"),
target, platform, architecture, configuration, logFile);
#if PLATFORM_WINDOWS
if (data.Platform == BuildPlatform::LinuxX64)
{
// Skip building C++ for Linux on Windows (no need to install cross-toolchain to build C# game)
args += TEXT(" -BuildBindingsOnly");
// Assume FlaxGame was prebuilt for Linux
args += TEXT(" -SkipTargets=FlaxGame");
}
#endif
if (ScriptsBuilder::RunBuildTool(args))
{
data.Error(TEXT("Failed to compile game scripts."));
return true;
}
BUILD_STEP_CANCEL_CHECK;
if (data.Tools->OnScriptsCompilationEnd(data))
return true;
data.StepProgress(TEXT("Exporting binaries"), 0.8f);
// Deploy binary modules
const String targetBuildInfo = project->ProjectFolderPath / TEXT("Binaries") / project->GameTarget / platform / architecture / configuration / target + TEXT(".Build.json");
if (DeployBinaries(data, targetBuildInfo, project->ProjectFolderPath))
return true;
data.StepProgress(TEXT("Generating merged build info"), 0.95f);
// Generate merged build info for all deployed binary modules
{
rapidjson_flax::StringBuffer buffer;
#if BUILD_DEBUG
PrettyJsonWriter writerObj(buffer);
#else
CompactJsonWriter writerObj(buffer);
#endif
JsonWriter& writer = writerObj;
writer.StartObject();
{
writer.JKEY("Name");
writer.String(target);
writer.JKEY("Platform");
writer.String(platform);
writer.JKEY("Configuration");
writer.String(configuration);
writer.JKEY("BinaryModules");
writer.StartArray();
for (auto& binaryModule : data.BinaryModules)
{
writer.StartObject();
writer.JKEY("Name");
writer.String(binaryModule.Name);
if (binaryModule.NativePath.HasChars())
{
writer.JKEY("NativePath");
writer.String(binaryModule.NativePath);
}
if (binaryModule.ManagedPath.HasChars())
{
writer.JKEY("ManagedPath");
writer.String(binaryModule.ManagedPath);
}
writer.EndObject();
}
writer.EndArray();
}
writer.EndObject();
const String outputBuildInfo = data.OutputPath / TEXT("Game.Build.json");
if (File::WriteAllBytes(outputBuildInfo, (byte*)buffer.GetString(), (int32)buffer.GetSize()))
{
LOG(Error, "Failed to save binary modules info file {0}.", outputBuildInfo);
return true;
}
}
if (data.Tools->OnScriptsStepDone(data))
return true;
return false;
}

View File

@@ -0,0 +1,24 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Editor/Cooker/GameCooker.h"
/// <summary>
/// Game scripts compilation step. Outputs proper assemblies compiled to the target platform.
/// </summary>
/// <seealso cref="GameCooker::BuildStep" />
class CompileScriptsStep : public GameCooker::BuildStep
{
private:
Array<String, FixedAllocation<8>> _extensionsToSkip;
Array<String, InlinedAllocation<32>> _deployedBuilds;
bool DeployBinaries(CookingData& data, const String& path, const String& projectFolderPath);
public:
// [BuildStep]
bool Perform(CookingData& data) override;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,187 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Editor/Cooker/GameCooker.h"
#include "Engine/Core/Types/Pair.h"
#include "Engine/Core/Types/DateTime.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Content/AssetInfo.h"
#include "Engine/Content/Cache/AssetsCache.h"
class Asset;
class BinaryAsset;
class JsonAssetBase;
struct AssetInitData;
/// <summary>
/// Cooking step that builds all the assets and packages them to the output directory.
/// Uses incremental build cache to provide faster building.
/// </summary>
/// <seealso cref="GameCooker::BuildStep" />
class FLAXENGINE_API CookAssetsStep : public GameCooker::BuildStep
{
public:
typedef Array<Pair<String, DateTime>> FileDependenciesList;
/// <summary>
/// Cached cooked asset entry data.
/// </summary>
struct FLAXENGINE_API CacheEntry
{
/// <summary>
/// The asset identifier.
/// </summary>
Guid ID;
/// <summary>
/// The stored data full typename. Used to recognize asset type.
/// </summary>
String TypeName;
/// <summary>
/// The asset file modification time.
/// </summary>
DateTime FileModified;
/// <summary>
/// The list of files on which this entry depends on. Cached date is the last edit time used to discard cache result on modification.
/// </summary>
FileDependenciesList FileDependencies;
};
/// <summary>
/// Assets cooking cache data (incremental building feature).
/// </summary>
struct FLAXENGINE_API CacheData
{
/// <summary>
/// The cache header file path.
/// </summary>
String HeaderFilePath;
/// <summary>
/// The cached files folder.
/// </summary>
String CacheFolder;
/// <summary>
/// The build options used to cook assets. Changing some options in game settings might trigger cached assets invalidation.
/// </summary>
struct
{
struct
{
bool SupportDX11;
bool SupportDX10;
bool SupportVulkan;
} Windows;
struct
{
bool SupportDX11;
bool SupportDX10;
} UWP;
struct
{
bool SupportVulkan;
} Linux;
struct
{
bool ShadersNoOptimize;
bool ShadersGenerateDebugData;
} Global;
} Settings;
/// <summary>
/// The cached entries.
/// </summary>
Dictionary<Guid, CacheEntry> Entries;
public:
/// <summary>
/// Gets the path to the asset of the given id (file may be missing).
/// </summary>
/// <param name="id">The asset id.</param>
/// <param name="cachedFilePath">The cached file path to use for creating cache storage.</param>
void GetFilePath(const Guid& id, String& cachedFilePath) const
{
cachedFilePath = CacheFolder / id.ToString(Guid::FormatType::N);
}
/// <summary>
/// Creates the new entry for the cooked asset file.
/// </summary>
/// <param name="asset">The asset.</param>
/// <param name="cachedFilePath">The cached file path to use for creating cache storage.</param>
/// <returns>The added entry reference.</returns>
CacheEntry& CreateEntry(const JsonAssetBase* asset, String& cachedFilePath);
/// <summary>
/// Creates the new entry for the cooked asset file.
/// </summary>
/// <param name="asset">The asset.</param>
/// <param name="cachedFilePath">The cached file path to use for creating cache storage.</param>
/// <returns>The added entry reference.</returns>
CacheEntry& CreateEntry(const Asset* asset, String& cachedFilePath);
/// <summary>
/// Removes all cached entries for assets that contain a shader. This forces rebuild for them.
/// </summary>
void InvalidateShaders();
/// <summary>
/// Loads the cache for the given cooking data.
/// </summary>
/// <param name="data">The data.</param>
void Load(CookingData& data);
/// <summary>
/// Saves this cache (header file).
/// </summary>
void Save();
};
struct FLAXENGINE_API AssetCookData
{
CookingData& Data;
CacheData& Cache;
AssetInitData& InitData;
Asset* Asset;
FileDependenciesList& FileDependencies;
};
typedef bool (*ProcessAssetFunc)(AssetCookData&);
/// <summary>
/// The asset processors (key: asset full typename, value: processor function that cooks the asset).
/// </summary>
static Dictionary<String, ProcessAssetFunc> AssetProcessors;
static bool ProcessDefaultAsset(AssetCookData& options);
private:
AssetsCache::Registry AssetsRegistry;
AssetsCache::PathsMapping AssetPathsMapping;
bool Process(CookingData& data, CacheData& cache, Asset* asset);
bool Process(CookingData& data, CacheData& cache, BinaryAsset* asset);
bool Process(CookingData& data, CacheData& cache, JsonAssetBase* asset);
public:
/// <summary>
/// Initializes a new instance of the <see cref="CookAssetsStep"/> class.
/// </summary>
CookAssetsStep();
public:
// [BuildStep]
bool Perform(CookingData& data) override;
};

View File

@@ -0,0 +1,120 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "DeployDataStep.h"
#include "Engine/Platform/FileSystem.h"
#include "Editor/Cooker/PlatformTools.h"
#include "Engine/Core/Config/BuildSettings.h"
#include "Engine/Core/Config/GameSettings.h"
#include "Engine/Renderer/ReflectionsPass.h"
#include "Engine/Renderer/AntiAliasing/SMAA.h"
bool DeployDataStep::Perform(CookingData& data)
{
data.StepProgress(TEXT("Deploying engine data"), 0);
const String depsRoot = data.GetPlatformBinariesRoot();
// Setup output folders and copy required data
const auto contentDir = data.OutputPath / TEXT("Content");
if (FileSystem::DirectoryExists(contentDir))
{
// Remove old content files
FileSystem::DeleteDirectory(contentDir, true);
// Give some time for Explorer (if location was viewed)
Platform::Sleep(10);
}
FileSystem::CreateDirectory(contentDir);
const auto srcMono = depsRoot / TEXT("Mono");
const auto dstMono = data.OutputPath / TEXT("Mono");
if (!FileSystem::DirectoryExists(dstMono))
{
if (!FileSystem::DirectoryExists(srcMono))
{
data.Error(TEXT("Missing Mono runtime data files."));
return true;
}
if (FileSystem::CopyDirectory(dstMono, srcMono, true))
{
data.Error(TEXT("Failed to copy Mono runtime data files."));
return true;
}
}
// Deploy engine data for the target platform
if (data.Tools->OnDeployBinaries(data))
return true;
// Register engine in-build assets
data.AddRootEngineAsset(TEXT("Shaders/AtmospherePreCompute"));
data.AddRootEngineAsset(TEXT("Shaders/ColorGrading"));
data.AddRootEngineAsset(TEXT("Shaders/DebugDraw"));
data.AddRootEngineAsset(TEXT("Shaders/DepthOfField"));
data.AddRootEngineAsset(TEXT("Shaders/EyeAdaptation"));
data.AddRootEngineAsset(TEXT("Shaders/Fog"));
data.AddRootEngineAsset(TEXT("Shaders/Forward"));
data.AddRootEngineAsset(TEXT("Shaders/FXAA"));
data.AddRootEngineAsset(TEXT("Shaders/TAA"));
data.AddRootEngineAsset(TEXT("Shaders/SMAA"));
data.AddRootEngineAsset(TEXT("Shaders/GBuffer"));
data.AddRootEngineAsset(TEXT("Shaders/GUI"));
data.AddRootEngineAsset(TEXT("Shaders/Histogram"));
data.AddRootEngineAsset(TEXT("Shaders/Lights"));
data.AddRootEngineAsset(TEXT("Shaders/MultiScaler"));
data.AddRootEngineAsset(TEXT("Shaders/PostProcessing"));
data.AddRootEngineAsset(TEXT("Shaders/MotionBlur"));
data.AddRootEngineAsset(TEXT("Shaders/BitonicSort"));
data.AddRootEngineAsset(TEXT("Shaders/GPUParticlesSorting"));
data.AddRootEngineAsset(TEXT("Shaders/Quad"));
data.AddRootEngineAsset(TEXT("Shaders/Reflections"));
data.AddRootEngineAsset(TEXT("Shaders/Shadows"));
data.AddRootEngineAsset(TEXT("Shaders/Sky"));
data.AddRootEngineAsset(TEXT("Shaders/SSAO"));
data.AddRootEngineAsset(TEXT("Shaders/SSR"));
data.AddRootEngineAsset(TEXT("Shaders/VolumetricFog"));
data.AddRootEngineAsset(TEXT("Engine/DefaultMaterial"));
data.AddRootEngineAsset(TEXT("Engine/DefaultTerrainMaterial"));
if (!GameSettings::NoSplashScreen && !GameSettings::SplashScreen.IsValid())
data.AddRootEngineAsset(TEXT("Engine/Textures/Logo"));
data.AddRootEngineAsset(TEXT("Engine/Textures/NormalTexture"));
data.AddRootEngineAsset(TEXT("Engine/Textures/BlackTexture"));
data.AddRootEngineAsset(TEXT("Engine/Textures/WhiteTexture"));
data.AddRootEngineAsset(TEXT("Engine/Textures/DefaultLensStarburst"));
data.AddRootEngineAsset(TEXT("Engine/Textures/DefaultLensColor"));
data.AddRootEngineAsset(TEXT("Engine/Textures/DefaultLensDirt"));
data.AddRootEngineAsset(TEXT("Engine/Textures/Bokeh/Circle"));
data.AddRootEngineAsset(TEXT("Engine/Textures/Bokeh/Hexagon"));
data.AddRootEngineAsset(TEXT("Engine/Textures/Bokeh/Octagon"));
data.AddRootEngineAsset(TEXT("Engine/Textures/Bokeh/Cross"));
data.AddRootEngineAsset(TEXT("Engine/Models/Sphere"));
data.AddRootEngineAsset(TEXT("Engine/Models/SphereLowPoly"));
data.AddRootEngineAsset(TEXT("Engine/Models/Box"));
data.AddRootEngineAsset(TEXT("Engine/Models/SimpleBox"));
data.AddRootEngineAsset(TEXT("Engine/Models/Quad"));
data.AddRootEngineAsset(TEXT("Engine/SkyboxMaterial"));
data.AddRootEngineAsset(PRE_INTEGRATED_GF_ASSET_NAME);
data.AddRootEngineAsset(SMAA_AREA_TEX);
data.AddRootEngineAsset(SMAA_SEARCH_TEX);
// Register game assets
data.StepProgress(TEXT("Deploying game data"), 50);
auto& buildSettings = *BuildSettings::Instance();
for (auto& e : buildSettings.AdditionalAssets)
data.AddRootAsset(e.GetID());
Array<String> files;
for (auto& e : buildSettings.AdditionalAssetFolders)
{
String path = FileSystem::ConvertRelativePathToAbsolute(Globals::ProjectFolder, e);
if (FileSystem::DirectoryGetFiles(files, path, TEXT("*"), DirectorySearchOption::AllDirectories))
{
data.Error(TEXT("Failed to find additional assets to deploy."));
return true;
}
for (auto& q : files)
data.AddRootAsset(q);
files.Clear();
}
return false;
}

View File

@@ -0,0 +1,17 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Editor/Cooker/GameCooker.h"
/// <summary>
/// Engine and game content and data files deployment step.
/// </summary>
/// <seealso cref="GameCooker::BuildStep" />
class DeployDataStep : public GameCooker::BuildStep
{
public:
// [BuildStep]
bool Perform(CookingData& data) override;
};

View File

@@ -0,0 +1,9 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "PostProcessStep.h"
#include "Editor/Cooker/PlatformTools.h"
bool PostProcessStep::Perform(CookingData& data)
{
return data.Tools->OnPostProcess(data);
}

View File

@@ -0,0 +1,17 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Editor/Cooker/GameCooker.h"
/// <summary>
/// Final cooking step that can perform custom set of actions on generated game data.
/// </summary>
/// <seealso cref="GameCooker::BuildStep" />
class PostProcessStep : public GameCooker::BuildStep
{
public:
// [BuildStep]
bool Perform(CookingData& data) override;
};

View File

@@ -0,0 +1,78 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "PrecompileAssembliesStep.h"
#include "Editor/Scripting/ScriptsBuilder.h"
#include "Engine/Platform/FileSystem.h"
#include "Editor/Cooker/PlatformTools.h"
bool PrecompileAssembliesStep::Perform(CookingData& data)
{
// Skip for some platforms
if (!data.Tools->UseAOT())
return false;
LOG(Info, "Using AOT...");
// Useful references about AOT:
// http://www.mono-project.com/docs/advanced/runtime/docs/aot/
// http://www.mono-project.com/docs/advanced/aot/
const String infoMsg = TEXT("Running AOT");
data.StepProgress(infoMsg, 0);
// Setup
PlatformTools::AotConfig config(data);
data.Tools->OnConfigureAOT(data, config);
// Prepare output directory
config.AotCachePath = data.OutputPath / TEXT("Mono/lib/mono/aot-cache");
switch (data.Tools->GetArchitecture())
{
case ArchitectureType::x86:
config.AotCachePath /= TEXT("x86");
break;
case ArchitectureType::x64:
config.AotCachePath /= TEXT("amd64");
break;
default:
data.Error(TEXT("Not supported AOT architecture"));
return true;
}
if (!FileSystem::DirectoryExists(config.AotCachePath))
{
if (FileSystem::CreateDirectory(config.AotCachePath))
{
data.Error(TEXT("Failed to setup AOT output directory."));
return true;
}
}
// Collect assemblies for AOT
// TODO: don't perform AOT on all assemblies but only ones used by the game and engine assemblies
for (auto& dir : config.AssembliesSearchDirs)
FileSystem::DirectoryGetFiles(config.Assemblies, dir, TEXT("*.dll"), DirectorySearchOption::TopDirectoryOnly);
for (auto& binaryModule : data.BinaryModules)
if (binaryModule.ManagedPath.HasChars())
config.Assemblies.Add(data.OutputPath / binaryModule.ManagedPath);
// TODO: move AOT to Flax.Build and perform it on all C# assemblies used in target build
config.Assemblies.Add(data.OutputPath / TEXT("Newtonsoft.Json.dll"));
// Perform AOT for the assemblies
for (int32 i = 0; i < config.Assemblies.Count(); i++)
{
BUILD_STEP_CANCEL_CHECK;
if (data.Tools->OnPerformAOT(data, config, config.Assemblies[i]))
return true;
data.StepProgress(infoMsg, static_cast<float>(i) / config.Assemblies.Count());
}
BUILD_STEP_CANCEL_CHECK;
if (data.Tools->OnPostProcessAOT(data, config))
return true;
// TODO: maybe remove GAC/assemblies? aot-cache could be only used in the build game
return false;
}

View File

@@ -0,0 +1,18 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Editor/Cooker/GameCooker.h"
/// <summary>
/// Optional step used only on selected platform that precompiles C# script assemblies.
/// Uses Mono Ahead of Time Compilation (AOT) feature.
/// </summary>
/// <seealso cref="GameCooker::BuildStep" />
class PrecompileAssembliesStep : public GameCooker::BuildStep
{
public:
// [BuildStep]
bool Perform(CookingData& data) override;
};

View File

@@ -0,0 +1,73 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#include "ValidateStep.h"
#include "Engine/Core/Config/GameSettings.h"
#include "Engine/Content/Content.h"
#include "Engine/Engine/Globals.h"
#include "Engine/Platform/FileSystem.h"
bool ValidateStep::Perform(CookingData& data)
{
data.StepProgress(TEXT("Performing validation"), 0);
// Ensure output and cache directories exist
if (!FileSystem::DirectoryExists(data.OutputPath))
{
if (FileSystem::CreateDirectory(data.OutputPath))
{
data.Error(TEXT("Failed to create build output directory."));
return true;
}
}
if (!FileSystem::DirectoryExists(data.CacheDirectory))
{
if (FileSystem::CreateDirectory(data.CacheDirectory))
{
data.Error(TEXT("Failed to create build cache directory."));
return true;
}
}
#if OFFICIAL_BUILD
// Validate that platform data is installed
if (!FileSystem::DirectoryExists(data.GetGameBinariesPath()))
{
data.Error(TEXT("Missing platform data tools for the target platform. Use Flax Launcher and download the required package."));
return true;
}
#endif
// Load game settings (may be modified via editor)
GameSettings::Load();
data.AddRootAsset(Globals::ProjectContentFolder / TEXT("GameSettings.json"));
// Validate game settings
{
if (GameSettings::ProductName.IsEmpty())
{
data.Error(TEXT("Missing product name."));
return true;
}
if (GameSettings::CompanyName.IsEmpty())
{
data.Error(TEXT("Missing company name."));
return true;
}
// TODO: validate version
AssetInfo info;
if (!Content::GetAssetInfo(GameSettings::FirstScene, info))
{
data.Error(TEXT("Missing first scene."));
return true;
}
}
// TODO: validate more game config
// TODO: validate all input scenes?
return false;
}

View File

@@ -0,0 +1,17 @@
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
#pragma once
#include "Editor/Cooker/GameCooker.h"
/// <summary>
/// Project data validation step. Ensures that game cooking can be started.
/// </summary>
/// <seealso cref="GameCooker::BuildStep" />
class ValidateStep : public GameCooker::BuildStep
{
public:
// [BuildStep]
bool Perform(CookingData& data) override;
};