Add **Web platform with Emscripten**
This commit is contained in:
BIN
Content/Shaders/Reflections.flax
LFS
BIN
Content/Shaders/Reflections.flax
LFS
Binary file not shown.
@@ -141,6 +141,11 @@ API_ENUM() enum class BuildPlatform
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"Windows ARM64\")")
|
||||
WindowsARM64 = 15,
|
||||
|
||||
/// <summary>
|
||||
/// Web
|
||||
/// </summary>
|
||||
Web = 16,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -69,6 +69,10 @@
|
||||
#include "Platform/iOS/iOSPlatformTools.h"
|
||||
#include "Engine/Platform/iOS/iOSPlatformSettings.h"
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_WEB
|
||||
#include "Platform/Web/WebPlatformTools.h"
|
||||
#include "Engine/Platform/Web/WebPlatformSettings.h"
|
||||
#endif
|
||||
|
||||
namespace GameCookerImpl
|
||||
{
|
||||
@@ -151,6 +155,8 @@ const Char* ToString(const BuildPlatform platform)
|
||||
return TEXT("iOS ARM64");
|
||||
case BuildPlatform::WindowsARM64:
|
||||
return TEXT("Windows ARM64");
|
||||
case BuildPlatform::Web:
|
||||
return TEXT("Web");
|
||||
default:
|
||||
return TEXT("");
|
||||
}
|
||||
@@ -307,6 +313,10 @@ void CookingData::GetBuildPlatformName(const Char*& platform, const Char*& archi
|
||||
platform = TEXT("Windows");
|
||||
architecture = TEXT("ARM64");
|
||||
break;
|
||||
case BuildPlatform::Web:
|
||||
platform = TEXT("Web");
|
||||
architecture = TEXT("x86");
|
||||
break;
|
||||
default:
|
||||
LOG(Fatal, "Unknown or unsupported build platform.");
|
||||
}
|
||||
@@ -461,6 +471,11 @@ PlatformTools* GameCooker::GetTools(BuildPlatform platform)
|
||||
case BuildPlatform::iOSARM64:
|
||||
result = New<iOSPlatformTools>();
|
||||
break;
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_WEB
|
||||
case BuildPlatform::Web:
|
||||
result = New<WebPlatformTools>();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
Tools.Add(platform, result);
|
||||
@@ -604,6 +619,9 @@ void GameCooker::GetCurrentPlatform(PlatformType& platform, BuildPlatform& build
|
||||
case PlatformType::iOS:
|
||||
buildPlatform = BuildPlatform::iOSARM64;
|
||||
break;
|
||||
case PlatformType::Web:
|
||||
buildPlatform = BuildPlatform::Web;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ namespace FlaxEditor
|
||||
case BuildPlatform.MacOSARM64:
|
||||
case BuildPlatform.MacOSx64: return PlatformType.Mac;
|
||||
case BuildPlatform.iOSARM64: return PlatformType.iOS;
|
||||
case BuildPlatform.Web: return PlatformType.Web;
|
||||
default: throw new ArgumentOutOfRangeException(nameof(buildPlatform), buildPlatform, null);
|
||||
}
|
||||
}
|
||||
|
||||
60
Source/Editor/Cooker/Platform/Web/WebPlatformTools.cpp
Normal file
60
Source/Editor/Cooker/Platform/Web/WebPlatformTools.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_TOOLS_WEB
|
||||
|
||||
#include "WebPlatformTools.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Platform/Web/WebPlatformSettings.h"
|
||||
#include "Engine/Core/Config/GameSettings.h"
|
||||
#include "Engine/Core/Config/BuildSettings.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/JsonAsset.h"
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
|
||||
IMPLEMENT_SETTINGS_GETTER(WebPlatformSettings, WebPlatform);
|
||||
|
||||
const Char* WebPlatformTools::GetDisplayName() const
|
||||
{
|
||||
return TEXT("Web");
|
||||
}
|
||||
|
||||
const Char* WebPlatformTools::GetName() const
|
||||
{
|
||||
return TEXT("Web");
|
||||
}
|
||||
|
||||
PlatformType WebPlatformTools::GetPlatform() const
|
||||
{
|
||||
return PlatformType::Web;
|
||||
}
|
||||
|
||||
ArchitectureType WebPlatformTools::GetArchitecture() const
|
||||
{
|
||||
return ArchitectureType::x86;
|
||||
}
|
||||
|
||||
DotNetAOTModes WebPlatformTools::UseAOT() const
|
||||
{
|
||||
return DotNetAOTModes::MonoAOTStatic;
|
||||
}
|
||||
|
||||
PixelFormat WebPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format)
|
||||
{
|
||||
// TODO: texture compression for Web (eg. ASTC for mobile and BC for others?)
|
||||
return PixelFormatExtensions::FindUncompressedFormat(format);
|
||||
}
|
||||
|
||||
bool WebPlatformTools::IsNativeCodeFile(CookingData& data, const String& file)
|
||||
{
|
||||
String extension = FileSystem::GetExtension(file);
|
||||
return extension.IsEmpty() || extension == TEXT("html") || extension == TEXT("js") || extension == TEXT("wams");
|
||||
}
|
||||
|
||||
bool WebPlatformTools::OnPostProcess(CookingData& data)
|
||||
{
|
||||
// TODO: customizable HTML templates
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
26
Source/Editor/Cooker/Platform/Web/WebPlatformTools.h
Normal file
26
Source/Editor/Cooker/Platform/Web/WebPlatformTools.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_TOOLS_WEB
|
||||
|
||||
#include "../../PlatformTools.h"
|
||||
|
||||
/// <summary>
|
||||
/// The Web platform support tools.
|
||||
/// </summary>
|
||||
class WebPlatformTools : public PlatformTools
|
||||
{
|
||||
public:
|
||||
// [PlatformTools]
|
||||
const Char* GetDisplayName() const override;
|
||||
const Char* GetName() const override;
|
||||
PlatformType GetPlatform() const override;
|
||||
ArchitectureType GetArchitecture() const override;
|
||||
DotNetAOTModes UseAOT() const override;
|
||||
PixelFormat GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) override;
|
||||
bool IsNativeCodeFile(CookingData& data, const String& file) override;
|
||||
bool OnPostProcess(CookingData& data) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -572,6 +572,14 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
|
||||
COMPILE_PROFILE(Vulkan_SM5, SHADER_FILE_CHUNK_INTERNAL_VULKAN_SM5_CACHE);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#if PLATFORM_TOOLS_WEB
|
||||
case BuildPlatform::Web:
|
||||
{
|
||||
const char* platformDefineName = "PLATFORM_WEB";
|
||||
// TODO: compile shaders for WebGPU
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
{
|
||||
|
||||
@@ -87,6 +87,7 @@ public class Editor : EditorModule
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID");
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "iOS", "PLATFORM_TOOLS_IOS");
|
||||
}
|
||||
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Web", "PLATFORM_TOOLS_WEB");
|
||||
|
||||
// Visual Studio integration
|
||||
if (options.Platform.Target == TargetPlatform.Windows && Flax.Build.Platform.BuildTargetPlatform == TargetPlatform.Windows)
|
||||
|
||||
@@ -93,6 +93,7 @@ namespace FlaxEditor.GUI
|
||||
new PlatformData(PlatformType.PS5, icons.PS5Icon128, "PlayStation 5"),
|
||||
new PlatformData(PlatformType.Mac, icons.MacOSIcon128, "macOS"),
|
||||
new PlatformData(PlatformType.iOS, icons.IOSIcon128, "iOS"),
|
||||
new PlatformData(PlatformType.Web, icons.Flax64, "Web"),
|
||||
};
|
||||
|
||||
const float IconSize = 64.0f;
|
||||
|
||||
@@ -1603,9 +1603,9 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Platform Switch",
|
||||
Description = "Gets the input value based on the runtime-platform type",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(220, 240),
|
||||
Size = new Float2(220, 260),
|
||||
ConnectionsHints = ConnectionsHint.Value,
|
||||
IndependentBoxes = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 },
|
||||
IndependentBoxes = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 },
|
||||
DependentBoxes = new[] { 0 },
|
||||
Elements = new[]
|
||||
{
|
||||
@@ -1622,6 +1622,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
NodeElementArchetype.Factory.Input(9, "PlayStation 5", true, null, 10),
|
||||
NodeElementArchetype.Factory.Input(10, "Mac", true, null, 11),
|
||||
NodeElementArchetype.Factory.Input(11, "iOS", true, null, 12),
|
||||
NodeElementArchetype.Factory.Input(12, "Web", true, null, 13),
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace FlaxEditor.Windows
|
||||
{ PlatformType.PS5, new PS5() },
|
||||
{ PlatformType.Mac, new Mac() },
|
||||
{ PlatformType.iOS, new iOS() },
|
||||
{ PlatformType.Web, new Web() },
|
||||
};
|
||||
|
||||
public BuildTabProxy(GameCookerWindow win, PlatformSelector platformSelector)
|
||||
@@ -165,6 +166,7 @@ namespace FlaxEditor.Windows
|
||||
case BuildPlatform.UWPx64:
|
||||
case BuildPlatform.LinuxX64:
|
||||
case BuildPlatform.AndroidARM64:
|
||||
case BuildPlatform.Web:
|
||||
text += "\nUse Flax Launcher and download the required package.";
|
||||
break;
|
||||
#endif
|
||||
@@ -538,6 +540,11 @@ namespace FlaxEditor.Windows
|
||||
protected override BuildPlatform BuildPlatform => BuildPlatform.iOSARM64;
|
||||
}
|
||||
|
||||
class Web : Platform
|
||||
{
|
||||
protected override BuildPlatform BuildPlatform => BuildPlatform.Web;
|
||||
}
|
||||
|
||||
class Editor : CustomEditor
|
||||
{
|
||||
private PlatformType _platform;
|
||||
@@ -592,6 +599,9 @@ namespace FlaxEditor.Windows
|
||||
case PlatformType.iOS:
|
||||
name = "iOS";
|
||||
break;
|
||||
case PlatformType.Web:
|
||||
name = "Web";
|
||||
break;
|
||||
default:
|
||||
name = Utilities.Utils.GetPropertyNameUI(_platform.ToString());
|
||||
break;
|
||||
|
||||
@@ -60,6 +60,9 @@ public class Audio : EngineModule
|
||||
case TargetPlatform.iOS:
|
||||
useOpenAL = true;
|
||||
break;
|
||||
case TargetPlatform.Web:
|
||||
// TODO: audio playback on Web (OpenAL)
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
|
||||
@@ -194,6 +194,7 @@ bool AudioClip::ExtractData(Array<byte>& resultData, AudioDataInfo& resultDataIn
|
||||
|
||||
bool AudioClip::ExtractDataFloat(Array<float>& resultData, AudioDataInfo& resultDataInfo)
|
||||
{
|
||||
#if COMPILE_WITH_AUDIO_TOOL
|
||||
// Extract PCM data
|
||||
Array<byte> data;
|
||||
if (ExtractDataRaw(data, resultDataInfo))
|
||||
@@ -205,6 +206,9 @@ bool AudioClip::ExtractDataFloat(Array<float>& resultData, AudioDataInfo& result
|
||||
resultDataInfo.BitDepth = 32;
|
||||
|
||||
return false;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AudioClip::ExtractDataRaw(Array<byte>& resultData, AudioDataInfo& resultDataInfo)
|
||||
@@ -475,6 +479,7 @@ bool AudioClip::WriteBuffer(int32 chunkIndex)
|
||||
}
|
||||
info.NumSamples = Math::AlignDown(data.Length() / bytesPerSample, info.NumChannels * bytesPerSample);
|
||||
|
||||
#if COMPILE_WITH_AUDIO_TOOL
|
||||
// Convert to Mono if used as 3D source and backend doesn't support it
|
||||
if (Is3D() && info.NumChannels > 1 && EnumHasNoneFlags(AudioBackend::Features(), AudioBackend::FeatureFlags::SpatialMultiChannel))
|
||||
{
|
||||
@@ -486,6 +491,7 @@ bool AudioClip::WriteBuffer(int32 chunkIndex)
|
||||
info.NumSamples = samplesPerChannel;
|
||||
data = Span<byte>(tmp2.Get(), tmp2.Count());
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write samples to the audio buffer (create one if missing)
|
||||
Locker.Lock(); // StreamingTask loads buffers without lock so do it here
|
||||
|
||||
@@ -95,6 +95,8 @@ IMPLEMENT_ENGINE_SETTINGS_GETTER(SwitchPlatformSettings, SwitchPlatform);
|
||||
IMPLEMENT_ENGINE_SETTINGS_GETTER(MacPlatformSettings, MacPlatform);
|
||||
#elif PLATFORM_IOS
|
||||
IMPLEMENT_ENGINE_SETTINGS_GETTER(iOSPlatformSettings, iOSPlatform);
|
||||
#elif PLATFORM_WEB
|
||||
IMPLEMENT_ENGINE_SETTINGS_GETTER(WebPlatformSettings, WebPlatform);
|
||||
#else
|
||||
#error Unknown platform
|
||||
#endif
|
||||
@@ -280,6 +282,7 @@ void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
|
||||
DESERIALIZE(PS5Platform);
|
||||
DESERIALIZE(MacPlatform);
|
||||
DESERIALIZE(iOSPlatform);
|
||||
DESERIALIZE(WebPlatform);
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
@@ -210,6 +210,14 @@ namespace FlaxEditor.Content.Settings
|
||||
public JsonAsset iOSPlatform;
|
||||
#endif
|
||||
|
||||
#if FLAX_EDITOR || PLATFORM_WEB
|
||||
/// <summary>
|
||||
/// Reference to <see cref="WebPlatformSettings"/> asset. Used to apply configuration on Web platform.
|
||||
/// </summary>
|
||||
[EditorOrder(2110), EditorDisplay("Platform Settings", "Web"), AssetReference(typeof(WebPlatformSettings), true), Tooltip("Reference to Web Platform Settings asset")]
|
||||
public JsonAsset WebPlatform;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Gets the absolute path to the game settings asset file.
|
||||
/// </summary>
|
||||
@@ -345,6 +353,10 @@ namespace FlaxEditor.Content.Settings
|
||||
if (type == typeof(iOSPlatformSettings))
|
||||
return Load<iOSPlatformSettings>(gameSettings.iOSPlatform) as T;
|
||||
#endif
|
||||
#if FLAX_EDITOR || PLATFORM_WEB
|
||||
if (type == typeof(WebPlatformSettings))
|
||||
return Load<WebPlatformSettings>(gameSettings.WebPlatform) as T;
|
||||
#endif
|
||||
|
||||
if (gameSettings.CustomSettings != null)
|
||||
{
|
||||
@@ -443,6 +455,10 @@ namespace FlaxEditor.Content.Settings
|
||||
if (type == typeof(iOSPlatformSettings))
|
||||
return gameSettings.iOSPlatform;
|
||||
#endif
|
||||
#if FLAX_EDITOR || PLATFORM_WEB
|
||||
if (type == typeof(WebPlatformSettings))
|
||||
return gameSettings.WebPlatform;
|
||||
#endif
|
||||
|
||||
if (gameSettings.CustomSettings != null)
|
||||
{
|
||||
@@ -557,6 +573,8 @@ namespace FlaxEditor.Content.Settings
|
||||
return SaveAsset(gameSettings, ref gameSettings.MacPlatform, obj);
|
||||
if (type == typeof(iOSPlatformSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.iOSPlatform, obj);
|
||||
if (type == typeof(WebPlatformSettings))
|
||||
return SaveAsset(gameSettings, ref gameSettings.WebPlatform, obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ public:
|
||||
Guid PS5Platform;
|
||||
Guid MacPlatform;
|
||||
Guid iOSPlatform;
|
||||
Guid WebPlatform;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
|
||||
@@ -38,3 +38,6 @@
|
||||
#if PLATFORM_IOS
|
||||
#include "Engine/Platform/iOS/iOSPlatformSettings.h"
|
||||
#endif
|
||||
#if PLATFORM_WEB
|
||||
#include "Engine/Platform/Web/WebPlatformSettings.h"
|
||||
#endif
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#endif
|
||||
#include <iostream>
|
||||
|
||||
#define LOG_ENABLE_FILE (!PLATFORM_SWITCH)
|
||||
#define LOG_ENABLE_FILE (!PLATFORM_SWITCH && !PLATFORM_WEB)
|
||||
#define LOG_ENABLE_WINDOWS_SINGLE_NEW_LINE_CHAR (PLATFORM_WINDOWS && PLATFORM_DESKTOP && (USE_EDITOR || !BUILD_RELEASE))
|
||||
|
||||
namespace
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
typedef void* Tag;
|
||||
|
||||
template<typename T>
|
||||
class alignas(sizeof(void*)) Data
|
||||
class alignas(uint64) Data
|
||||
{
|
||||
private:
|
||||
byte _data[Capacity * sizeof(T)];
|
||||
@@ -200,13 +200,13 @@ public:
|
||||
typedef void* Tag;
|
||||
|
||||
template<typename T>
|
||||
class alignas(sizeof(void*)) Data
|
||||
class alignas(sizeof(uint64)) Data
|
||||
{
|
||||
private:
|
||||
typedef typename FallbackAllocation::template Data<T> FallbackData;
|
||||
|
||||
bool _useFallback = false;
|
||||
alignas(sizeof(void*)) byte _data[Capacity * sizeof(T)];
|
||||
alignas(sizeof(uint64)) byte _data[Capacity * sizeof(T)];
|
||||
FallbackData _fallback;
|
||||
|
||||
public:
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "Mac/MacGame.h"
|
||||
#elif PLATFORM_IOS
|
||||
#include "iOS/iOSGame.h"
|
||||
#elif PLATFORM_WEB
|
||||
#include "Web/WebGame.h"
|
||||
#else
|
||||
#error Missing Game implementation!
|
||||
#endif
|
||||
|
||||
19
Source/Engine/Engine/Web/WebGame.h
Normal file
19
Source/Engine/Engine/Web/WebGame.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WEB
|
||||
|
||||
#include "../Base/GameBase.h"
|
||||
|
||||
/// <summary>
|
||||
/// The game class implementation for Web platform.
|
||||
/// </summary>
|
||||
/// <seealso cref="Game" />
|
||||
class WebGame : public GameBase
|
||||
{
|
||||
};
|
||||
|
||||
typedef WebGame Game;
|
||||
|
||||
#endif
|
||||
@@ -45,7 +45,9 @@
|
||||
#define GPU_USE_WINDOW_SRV 1
|
||||
|
||||
// True if allow graphics profile events and markers
|
||||
#ifndef GPU_ALLOW_PROFILE_EVENTS
|
||||
#define GPU_ALLOW_PROFILE_EVENTS (!BUILD_RELEASE)
|
||||
#endif
|
||||
|
||||
// True if allow hardware tessellation shaders (Hull and Domain shaders)
|
||||
#ifndef GPU_ALLOW_TESSELLATION_SHADERS
|
||||
@@ -58,7 +60,9 @@
|
||||
#endif
|
||||
|
||||
// Enable/disable creating GPU resources on separate threads (otherwise only the main thread can be used)
|
||||
#ifndef GPU_ENABLE_ASYNC_RESOURCES_CREATION
|
||||
#define GPU_ENABLE_ASYNC_RESOURCES_CREATION 1
|
||||
#endif
|
||||
|
||||
// Enable/disable force shaders recompilation
|
||||
#define GPU_FORCE_RECOMPILE_SHADERS 0
|
||||
|
||||
@@ -96,6 +96,10 @@ public class Graphics : EngineModule
|
||||
else
|
||||
Log.WarningOnce(string.Format("Building for {0} without Vulkan rendering backend (Vulkan SDK is missing)", options.Platform.Target), ref _logMissingVulkanSDK);
|
||||
break;
|
||||
case TargetPlatform.Web:
|
||||
options.PrivateDependencies.Add("GraphicsDeviceNull");
|
||||
// TODO: add WebGPU
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ GPUTexture* RenderBuffers::RequestHiZ(GPUContext* context, bool fullRes, int32 m
|
||||
|
||||
// Allocate or resize buffer (with full mip-chain)
|
||||
// TODO: migrate to inverse depth and try using r16 again as default (should have no artifacts anymore)
|
||||
auto format = PLATFORM_ANDROID || PLATFORM_IOS || PLATFORM_SWITCH ? PixelFormat::R16_UInt : PixelFormat::R32_Float;
|
||||
auto format = PLATFORM_WEB || PLATFORM_ANDROID || PLATFORM_IOS || PLATFORM_SWITCH ? PixelFormat::R16_UInt : PixelFormat::R32_Float;
|
||||
auto width = fullRes ? _width : Math::Max(_width >> 1, 1);
|
||||
auto height = fullRes ? _height : Math::Max(_height >> 1, 1);
|
||||
auto desc = GPUTextureDescription::New2D(width, height, mipLevels, format, GPUTextureFlags::ShaderResource);
|
||||
|
||||
@@ -74,6 +74,9 @@ public class Main : EngineModule
|
||||
case TargetPlatform.iOS:
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "Default"));
|
||||
break;
|
||||
case TargetPlatform.Web:
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "Web"));
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
}
|
||||
|
||||
12
Source/Engine/Main/Web/main.cpp
Normal file
12
Source/Engine/Main/Web/main.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_WEB
|
||||
|
||||
#include "Engine/Engine/Engine.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
return Engine::Main(TEXT(""));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -39,6 +39,12 @@ public class Physics : EngineModule
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
if (options.Platform.Target == TargetPlatform.Web) // TODO: build PhysX for Web
|
||||
{
|
||||
options.PrivateDefinitions.Add("COMPILE_WITH_EMPTY_PHYSICS");
|
||||
return;
|
||||
}
|
||||
|
||||
SetupPhysicsBackend(this, options);
|
||||
|
||||
if (WithCooking)
|
||||
|
||||
@@ -99,11 +99,6 @@ public:
|
||||
static void Yield();
|
||||
static double GetTimeSeconds();
|
||||
static uint64 GetTimeCycles();
|
||||
FORCE_INLINE static uint64 GetClockFrequency()
|
||||
{
|
||||
// Dummy value
|
||||
return 1000000;
|
||||
}
|
||||
static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
|
||||
static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
|
||||
static bool Init();
|
||||
|
||||
@@ -44,7 +44,7 @@ static_assert(sizeof(double) == 8, "Invalid double type size.");
|
||||
|
||||
// Check configuration
|
||||
static_assert((PLATFORM_THREADS_LIMIT & (PLATFORM_THREADS_LIMIT - 1)) == 0, "Threads limit must be power of two.");
|
||||
static_assert(PLATFORM_THREADS_LIMIT % 4 == 0, "Threads limit must be multiple of 4.");
|
||||
static_assert(PLATFORM_THREADS_LIMIT % 4 == 0 || PLATFORM_THREADS_LIMIT == 1, "Threads limit must be multiple of 4.");
|
||||
|
||||
const Char* PlatformBase::ApplicationClassName = TEXT("FlaxWindow");
|
||||
float PlatformBase::CustomDpiScale = 1.0f;
|
||||
@@ -301,6 +301,15 @@ bool PlatformBase::Is64BitApp()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool PlatformBase::Is64BitPlatform()
|
||||
{
|
||||
#if PLATFORM_64BITS
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
int32 PlatformBase::GetCacheLineSize()
|
||||
{
|
||||
return (int32)Platform::GetCPUInfo().CacheLineSize;
|
||||
@@ -820,6 +829,8 @@ const Char* ToString(PlatformType type)
|
||||
return TEXT("Mac");
|
||||
case PlatformType::iOS:
|
||||
return TEXT("iOS");
|
||||
case PlatformType::Web:
|
||||
return TEXT("Web");
|
||||
default:
|
||||
return TEXT("");
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ public:
|
||||
/// Returns true if running on 64-bit computer
|
||||
/// </summary>
|
||||
/// <returns>True if running on 64-bit computer, otherwise false.</returns>
|
||||
API_PROPERTY() static bool Is64BitPlatform() = delete;
|
||||
API_PROPERTY() static bool Is64BitPlatform();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the operating system.
|
||||
@@ -470,7 +470,11 @@ public:
|
||||
/// Gets the system clock frequency.
|
||||
/// </summary>
|
||||
/// <returns>The clock frequency.</returns>
|
||||
API_PROPERTY() static uint64 GetClockFrequency() = delete;
|
||||
API_PROPERTY() static uint64 GetClockFrequency()
|
||||
{
|
||||
// Dummy value
|
||||
return 1000000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets current system time based on current computer settings.
|
||||
|
||||
@@ -48,7 +48,7 @@ void ThreadBase::Kill(bool waitForJoin)
|
||||
return;
|
||||
}
|
||||
ASSERT(GetID());
|
||||
const auto thread = static_cast<Thread*>(this);
|
||||
Thread* thread = (Thread*)this;
|
||||
|
||||
// Stop runnable object
|
||||
if (_callAfterWork && _runnable)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
|
||||
#include "Win32/Win32ConditionVariable.h"
|
||||
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS
|
||||
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS || PLATFORM_WEB
|
||||
#include "Unix/UnixConditionVariable.h"
|
||||
#elif PLATFORM_SWITCH
|
||||
#include "Platforms/Switch/Engine/Platform/SwitchConditionVariable.h"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
|
||||
#include "Win32/Win32CriticalSection.h"
|
||||
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS
|
||||
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS || PLATFORM_WEB
|
||||
#include "Unix/UnixCriticalSection.h"
|
||||
#elif PLATFORM_SWITCH
|
||||
#include "Platforms/Switch/Engine/Platform/SwitchCriticalSection.h"
|
||||
|
||||
@@ -67,6 +67,11 @@ API_ENUM() enum class PlatformType
|
||||
/// </summary>
|
||||
API_ENUM(Attributes="EditorDisplay(null, \"iOS\")")
|
||||
iOS = 11,
|
||||
|
||||
/// <summary>
|
||||
/// Running on Web.
|
||||
/// </summary>
|
||||
Web = 12,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -143,6 +148,9 @@ API_ENUM() enum class ArchitectureType
|
||||
#if !defined(PLATFORM_SDL)
|
||||
#define PLATFORM_SDL 0
|
||||
#endif
|
||||
#if !defined(PLATFORM_WEB)
|
||||
#define PLATFORM_WEB 0
|
||||
#endif
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Windows/WindowsDefines.h"
|
||||
@@ -166,6 +174,8 @@ API_ENUM() enum class ArchitectureType
|
||||
#include "Mac/MacDefines.h"
|
||||
#elif PLATFORM_IOS
|
||||
#include "iOS/iOSDefines.h"
|
||||
#elif PLATFORM_WEB
|
||||
#include "Web/WebDefines.h"
|
||||
#else
|
||||
#error Missing Defines implementation!
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
|
||||
#include "Win32/Win32File.h"
|
||||
#elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC
|
||||
#elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_WEB
|
||||
#include "Unix/UnixFile.h"
|
||||
#elif PLATFORM_IOS
|
||||
#include "iOS/iOSFile.h"
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "Mac/MacFileSystem.h"
|
||||
#elif PLATFORM_IOS
|
||||
#include "iOS/iOSFileSystem.h"
|
||||
#elif PLATFORM_WEB
|
||||
#include "Web/WebFileSystem.h"
|
||||
#else
|
||||
#error Missing File System implementation!
|
||||
#endif
|
||||
|
||||
@@ -114,11 +114,6 @@ public:
|
||||
static void Yield();
|
||||
static double GetTimeSeconds();
|
||||
static uint64 GetTimeCycles();
|
||||
FORCE_INLINE static uint64 GetClockFrequency()
|
||||
{
|
||||
// Dummy value
|
||||
return 1000000;
|
||||
}
|
||||
static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
|
||||
static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
|
||||
#if !BUILD_RELEASE
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
|
||||
#include "Win32/Win32Network.h"
|
||||
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_MAC || PLATFORM_IOS
|
||||
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_MAC || PLATFORM_IOS || PLATFORM_WEB
|
||||
#include "Unix/UnixNetwork.h"
|
||||
#elif PLATFORM_PS4
|
||||
#include "Platforms/PS4/Engine/Platform/PS4Network.h"
|
||||
|
||||
@@ -87,20 +87,17 @@ public class Platform : EngineModule
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "Apple"));
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "iOS"));
|
||||
break;
|
||||
case TargetPlatform.Web:
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "Unix"));
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "Web"));
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
if (EngineConfiguration.WithSDL(options))
|
||||
{
|
||||
switch (options.Platform.Target)
|
||||
{
|
||||
case TargetPlatform.Windows:
|
||||
case TargetPlatform.Linux:
|
||||
case TargetPlatform.Mac:
|
||||
options.PublicDependencies.Add("SDL");
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "SDL"));
|
||||
break;
|
||||
}
|
||||
options.PublicDependencies.Add("SDL");
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "SDL"));
|
||||
if (options.Platform.Target == TargetPlatform.Linux)
|
||||
options.PublicDependencies.Add("Wayland");
|
||||
}
|
||||
@@ -115,6 +112,7 @@ public class Platform : EngineModule
|
||||
options.SourceFiles.Add(Path.Combine(FolderPath, "Apple", "ApplePlatformSettings.h"));
|
||||
options.SourceFiles.Add(Path.Combine(FolderPath, "Mac", "MacPlatformSettings.h"));
|
||||
options.SourceFiles.Add(Path.Combine(FolderPath, "iOS", "iOSPlatformSettings.h"));
|
||||
options.SourceFiles.Add(Path.Combine(FolderPath, "Web", "WebPlatformSettings.h"));
|
||||
AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxOne", "Engine", "Platform", "XboxOnePlatformSettings.h"));
|
||||
AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxScarlett", "Engine", "Platform", "XboxScarlettPlatformSettings.h"));
|
||||
AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "PS4", "Engine", "Platform", "PS4PlatformSettings.h"));
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
#include "Mac/MacPlatform.h"
|
||||
#elif PLATFORM_IOS
|
||||
#include "iOS/iOSPlatform.h"
|
||||
#elif PLATFORM_WEB
|
||||
#include "Web/WebPlatform.h"
|
||||
#else
|
||||
#error Missing Platform implementation!
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
|
||||
#include "Win32/Win32ReadWriteLock.h"
|
||||
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS
|
||||
#elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5 || PLATFORM_MAC || PLATFORM_IOS || PLATFORM_WEB
|
||||
#include "Unix/UnixReadWriteLock.h"
|
||||
#elif PLATFORM_SWITCH
|
||||
#include "Platforms/Switch/Engine/Platform/SwitchReadWriteLock.h"
|
||||
|
||||
207
Source/Engine/Platform/SDL/SDLPlatform.Web.cpp
Normal file
207
Source/Engine/Platform/SDL/SDLPlatform.Web.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_SDL && PLATFORM_WEB
|
||||
|
||||
#include "SDLWindow.h"
|
||||
#include "Engine/Platform/MessageBox.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include <SDL3/SDL_messagebox.h>
|
||||
#include <SDL3/SDL_error.h>
|
||||
|
||||
bool SDLPlatform::InitInternal()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesWindows()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesWayland()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLPlatform::UsesX11()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLPlatform::PreHandleEvents()
|
||||
{
|
||||
}
|
||||
|
||||
void SDLPlatform::PostHandleEvents()
|
||||
{
|
||||
}
|
||||
|
||||
bool SDLWindow::HandleEventInternal(SDL_Event& event)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void SDLPlatform::SetHighDpiAwarenessEnabled(bool enable)
|
||||
{
|
||||
}
|
||||
|
||||
DragDropEffect SDLWindow::DoDragDrop(const StringView& data)
|
||||
{
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
DragDropEffect SDLWindow::DoDragDrop(const StringView& data, const Float2& offset, Window* dragSourceWindow)
|
||||
{
|
||||
Show();
|
||||
return DragDropEffect::None;
|
||||
}
|
||||
|
||||
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
|
||||
{
|
||||
StringAnsi textAnsi(text);
|
||||
StringAnsi captionAnsi(caption);
|
||||
|
||||
SDL_MessageBoxData data;
|
||||
SDL_MessageBoxButtonData dataButtons[3];
|
||||
data.window = nullptr;
|
||||
data.title = captionAnsi.GetText();
|
||||
data.message = textAnsi.GetText();
|
||||
data.colorScheme = nullptr;
|
||||
|
||||
switch (icon)
|
||||
{
|
||||
case MessageBoxIcon::Error:
|
||||
case MessageBoxIcon::Hand:
|
||||
case MessageBoxIcon::Stop:
|
||||
data.flags |= SDL_MESSAGEBOX_ERROR;
|
||||
break;
|
||||
case MessageBoxIcon::Asterisk:
|
||||
case MessageBoxIcon::Information:
|
||||
case MessageBoxIcon::Question:
|
||||
data.flags |= SDL_MESSAGEBOX_INFORMATION;
|
||||
break;
|
||||
case MessageBoxIcon::Exclamation:
|
||||
case MessageBoxIcon::Warning:
|
||||
data.flags |= SDL_MESSAGEBOX_WARNING;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (buttons)
|
||||
{
|
||||
case MessageBoxButtons::AbortRetryIgnore:
|
||||
dataButtons[0] =
|
||||
{
|
||||
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
|
||||
(int)DialogResult::Abort,
|
||||
"Abort"
|
||||
};
|
||||
dataButtons[1] =
|
||||
{
|
||||
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
|
||||
(int)DialogResult::Retry,
|
||||
"Retry"
|
||||
};
|
||||
dataButtons[2] =
|
||||
{
|
||||
0,
|
||||
(int)DialogResult::Ignore,
|
||||
"Ignore"
|
||||
};
|
||||
data.numbuttons = 3;
|
||||
break;
|
||||
case MessageBoxButtons::OK:
|
||||
dataButtons[0] =
|
||||
{
|
||||
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT | SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
|
||||
(int)DialogResult::OK,
|
||||
"OK"
|
||||
};
|
||||
data.numbuttons = 1;
|
||||
break;
|
||||
case MessageBoxButtons::OKCancel:
|
||||
dataButtons[0] =
|
||||
{
|
||||
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
|
||||
(int)DialogResult::OK,
|
||||
"OK"
|
||||
};
|
||||
dataButtons[1] =
|
||||
{
|
||||
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
|
||||
(int)DialogResult::Cancel,
|
||||
"Cancel"
|
||||
};
|
||||
data.numbuttons = 2;
|
||||
break;
|
||||
case MessageBoxButtons::RetryCancel:
|
||||
dataButtons[0] =
|
||||
{
|
||||
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
|
||||
(int)DialogResult::Retry,
|
||||
"Retry"
|
||||
};
|
||||
dataButtons[1] =
|
||||
{
|
||||
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
|
||||
(int)DialogResult::Cancel,
|
||||
"Cancel"
|
||||
};
|
||||
data.numbuttons = 2;
|
||||
break;
|
||||
case MessageBoxButtons::YesNo:
|
||||
dataButtons[0] =
|
||||
{
|
||||
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
|
||||
(int)DialogResult::Yes,
|
||||
"Yes"
|
||||
};
|
||||
dataButtons[1] =
|
||||
{
|
||||
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
|
||||
(int)DialogResult::No,
|
||||
"No"
|
||||
};
|
||||
data.numbuttons = 2;
|
||||
break;
|
||||
case MessageBoxButtons::YesNoCancel:
|
||||
{
|
||||
dataButtons[0] =
|
||||
{
|
||||
SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT,
|
||||
(int)DialogResult::Yes,
|
||||
"Yes"
|
||||
};
|
||||
dataButtons[1] =
|
||||
{
|
||||
0,
|
||||
(int)DialogResult::No,
|
||||
"No"
|
||||
};
|
||||
dataButtons[2] =
|
||||
{
|
||||
SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT,
|
||||
(int)DialogResult::Cancel,
|
||||
"Cancel"
|
||||
};
|
||||
data.numbuttons = 3;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
data.buttons = dataButtons;
|
||||
|
||||
int result = -1;
|
||||
if (!SDL_ShowMessageBox(&data, &result))
|
||||
{
|
||||
LOG(Error, "Failed to show SDL message box: {0}", String(SDL_GetError()));
|
||||
return DialogResult::Abort;
|
||||
}
|
||||
if (result < 0)
|
||||
return DialogResult::None;
|
||||
return (DialogResult)result;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -32,7 +32,7 @@
|
||||
namespace SDLImpl
|
||||
{
|
||||
int32 SystemDpi = 96;
|
||||
#if PLATFORM_LINUX
|
||||
#if PLATFORM_LINUX || PLATFORM_WEB
|
||||
String UserLocale("en");
|
||||
#endif
|
||||
bool WindowDecorationsSupported = true;
|
||||
@@ -109,7 +109,7 @@ bool SDLPlatform::Init()
|
||||
if (!SDL_InitSubSystem(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD))
|
||||
Platform::Fatal(String::Format(TEXT("Failed to initialize SDL: {0}."), String(SDL_GetError())));
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
#if PLATFORM_LINUX || PLATFORM_WEB
|
||||
int localesCount = 0;
|
||||
auto locales = SDL_GetPreferredLocales(&localesCount);
|
||||
for (int i = 0; i < localesCount; i++)
|
||||
@@ -241,7 +241,7 @@ int32 SDLPlatform::GetDpi()
|
||||
return SDLImpl::SystemDpi;
|
||||
}
|
||||
|
||||
#if PLATFORM_LINUX
|
||||
#if PLATFORM_LINUX || PLATFORM_WEB
|
||||
String SDLPlatform::GetUserLocaleName()
|
||||
{
|
||||
return SDLImpl::UserLocale;
|
||||
@@ -435,8 +435,10 @@ int32 SDLPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ARGS_POINTER, cmd.Get());
|
||||
SDL_SetPointerProperty(props, SDL_PROP_PROCESS_CREATE_ENVIRONMENT_POINTER, env);
|
||||
SDL_SetBooleanProperty(props, SDL_PROP_PROCESS_CREATE_BACKGROUND_BOOLEAN, background);
|
||||
#if !PLATFORM_WEB
|
||||
if (workingDirectory.HasChars())
|
||||
SDL_SetStringProperty(props, SDL_PROP_PROCESS_CREATE_WORKING_DIRECTORY_STRING, workingDirectory.Get());
|
||||
#endif
|
||||
if (captureStdOut)
|
||||
{
|
||||
SDL_SetNumberProperty(props, SDL_PROP_PROCESS_CREATE_STDOUT_NUMBER, SDL_PROCESS_STDIO_APP);
|
||||
|
||||
@@ -7,29 +7,25 @@
|
||||
#include "Engine/Platform/Base/Enums.h"
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "Engine/Platform/Windows/WindowsPlatform.h"
|
||||
typedef WindowsPlatform SDLPlatformBase;
|
||||
typedef struct tagMSG MSG;
|
||||
#elif PLATFORM_LINUX
|
||||
#include "Engine/Platform/Linux/LinuxPlatform.h"
|
||||
typedef LinuxPlatform SDLPlatformBase;
|
||||
union _XEvent;
|
||||
#elif PLATFORM_MAC
|
||||
#include "Engine/Platform/Mac/MacPlatform.h"
|
||||
typedef MacPlatform SDLPlatformBase;
|
||||
#elif PLATFORM_WEB
|
||||
#include "Engine/Platform/Web/WebPlatform.h"
|
||||
typedef WebPlatform SDLPlatformBase;
|
||||
#else
|
||||
static_assert(false, "Unsupported Platform");
|
||||
static_assert(false, "Unsupported SDL platform.");
|
||||
#endif
|
||||
|
||||
class SDLWindow;
|
||||
union SDL_Event;
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
typedef WindowsPlatform SDLPlatformBase;
|
||||
#elif PLATFORM_LINUX
|
||||
typedef LinuxPlatform SDLPlatformBase;
|
||||
#elif PLATFORM_MAC
|
||||
typedef MacPlatform SDLPlatformBase;
|
||||
#else
|
||||
static_assert(false, "Unsupported SDL platform.");
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The SDL platform implementation and application management utilities.
|
||||
/// </summary>
|
||||
@@ -84,7 +80,7 @@ public:
|
||||
static BatteryInfo GetBatteryInfo();
|
||||
#endif
|
||||
static int32 GetDpi();
|
||||
#if PLATFORM_LINUX
|
||||
#if PLATFORM_LINUX || PLATFORM_WEB
|
||||
static String GetUserLocaleName();
|
||||
#endif
|
||||
static bool CanOpenUrl(const StringView& url);
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "Engine/Platform/Linux/IncludeX11.h"
|
||||
#elif PLATFORM_MAC
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#elif PLATFORM_WEB
|
||||
#else
|
||||
static_assert(false, "Unsupported Platform");
|
||||
#endif
|
||||
@@ -77,6 +78,8 @@ void* GetNativeWindowPointer(SDL_Window* window)
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_ANDROID_WINDOW_POINTER, nullptr);
|
||||
#elif PLATFORM_IOS
|
||||
windowPtr = SDL_GetPointerProperty(props, SDL_PROP_WINDOW_UIKIT_WINDOW_POINTER, nullptr);
|
||||
#elif PLATFORM_WEB
|
||||
windowPtr = (void*)1; // Mock value (TODO: consider SDL_PROP_WINDOW_EMSCRIPTEN_CANVAS_ID_STRING)
|
||||
#else
|
||||
static_assert(false, "unsupported platform");
|
||||
#endif
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "Platforms/Switch/Engine/Platform/SwitchThread.h"
|
||||
#elif PLATFORM_MAC || PLATFORM_IOS
|
||||
#include "Apple/AppleThread.h"
|
||||
#elif PLATFORM_WEB
|
||||
#include "Web/WebThread.h"
|
||||
#else
|
||||
#error Missing Thread implementation!
|
||||
#endif
|
||||
|
||||
@@ -305,6 +305,29 @@ typedef UnixNetwork Network;
|
||||
class UserBase;
|
||||
typedef UserBase User;
|
||||
|
||||
#elif PLATFORM_WEB
|
||||
|
||||
class UnixCriticalSection;
|
||||
typedef UnixCriticalSection CriticalSection;
|
||||
class UnixReadWriteLock;
|
||||
typedef UnixReadWriteLock ReadWriteLock;
|
||||
class UnixConditionVariable;
|
||||
typedef UnixConditionVariable ConditionVariable;
|
||||
class WebFileSystem;
|
||||
typedef WebFileSystem FileSystem;
|
||||
class FileSystemWatcherBase;
|
||||
typedef FileSystemWatcherBase FileSystemWatcher;
|
||||
class UnixFile;
|
||||
typedef UnixFile File;
|
||||
class WebThread;
|
||||
typedef WebThread Thread;
|
||||
class ClipboardBase;
|
||||
typedef ClipboardBase Clipboard;
|
||||
class UnixNetwork;
|
||||
typedef UnixNetwork Network;
|
||||
class UserBase;
|
||||
typedef UserBase User;
|
||||
|
||||
#else
|
||||
|
||||
#error Missing Types implementation!
|
||||
|
||||
30
Source/Engine/Platform/Web/WebDefines.h
Normal file
30
Source/Engine/Platform/Web/WebDefines.h
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WEB
|
||||
|
||||
#include "../Unix/UnixDefines.h"
|
||||
|
||||
// Platform description
|
||||
#define PLATFORM_TYPE PlatformType::Web
|
||||
#define PLATFORM_64BITS 0
|
||||
#define PLATFORM_ARCH ArchitectureType::x86
|
||||
#define PLATFORM_CACHE_LINE_SIZE 64
|
||||
#define PLATFORM_DEBUG_BREAK
|
||||
#define PLATFORM_OUT_OF_MEMORY_BUFFER_SIZE 0
|
||||
|
||||
// Configure graphics
|
||||
#define GPU_ALLOW_TESSELLATION_SHADERS 0
|
||||
#define GPU_ALLOW_GEOMETRY_SHADERS 0
|
||||
#define GPU_ALLOW_PROFILE_EVENTS 0
|
||||
|
||||
// Threading is optional
|
||||
#ifdef __EMSCRIPTEN_PTHREADS__
|
||||
#define PLATFORM_THREADS_LIMIT 4
|
||||
#else
|
||||
#define PLATFORM_THREADS_LIMIT 1
|
||||
#define GPU_ENABLE_ASYNC_RESOURCES_CREATION 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
19
Source/Engine/Platform/Web/WebFileSystem.h
Normal file
19
Source/Engine/Platform/Web/WebFileSystem.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WEB
|
||||
|
||||
#include "Engine/Platform/Unix/UnixFileSystem.h"
|
||||
|
||||
/// <summary>
|
||||
/// Web platform implementation of filesystem service.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API WebFileSystem : public UnixFileSystem
|
||||
{
|
||||
public:
|
||||
// [UnixFileSystem]
|
||||
static void GetSpecialFolderPath(const SpecialFolder type, String& result);
|
||||
};
|
||||
|
||||
#endif
|
||||
254
Source/Engine/Platform/Web/WebPlatform.cpp
Normal file
254
Source/Engine/Platform/Web/WebPlatform.cpp
Normal file
@@ -0,0 +1,254 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#if PLATFORM_WEB
|
||||
|
||||
#include "WebPlatform.h"
|
||||
#include "WebFileSystem.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Types/Version.h"
|
||||
#include "Engine/Core/Types/TimeSpan.h"
|
||||
#include "Engine/Core/Types/Guid.h"
|
||||
#include "Engine/Platform/CPUInfo.h"
|
||||
#include "Engine/Platform/MemoryStats.h"
|
||||
#if !BUILD_RELEASE
|
||||
#include "Engine/Core/Types/StringView.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#endif
|
||||
#include <chrono>
|
||||
#include <emscripten/emscripten.h>
|
||||
#include <emscripten/threading.h>
|
||||
#include <emscripten/version.h>
|
||||
#include <emscripten/heap.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
CPUInfo Cpu;
|
||||
};
|
||||
|
||||
void WebFileSystem::GetSpecialFolderPath(const SpecialFolder type, String& result)
|
||||
{
|
||||
result = TEXT("/");
|
||||
}
|
||||
|
||||
String WebPlatform::GetSystemName()
|
||||
{
|
||||
return TEXT("Browser");
|
||||
}
|
||||
|
||||
Version WebPlatform::GetSystemVersion()
|
||||
{
|
||||
return Version(1, 0);
|
||||
}
|
||||
|
||||
CPUInfo WebPlatform::GetCPUInfo()
|
||||
{
|
||||
return Cpu;
|
||||
}
|
||||
|
||||
MemoryStats WebPlatform::GetMemoryStats()
|
||||
{
|
||||
// Mock memory stats
|
||||
MemoryStats result;
|
||||
result.TotalPhysicalMemory = emscripten_get_heap_max();
|
||||
result.UsedPhysicalMemory = emscripten_get_heap_size();
|
||||
result.TotalVirtualMemory = result.TotalPhysicalMemory;
|
||||
result.UsedVirtualMemory = result.UsedPhysicalMemory;
|
||||
result.ProgramSizeMemory = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
ProcessMemoryStats WebPlatform::GetProcessMemoryStats()
|
||||
{
|
||||
// Mock memory stats
|
||||
ProcessMemoryStats result;
|
||||
result.UsedPhysicalMemory = 1 * 1024 * 1024;
|
||||
result.UsedVirtualMemory = result.UsedPhysicalMemory;
|
||||
return result;
|
||||
}
|
||||
|
||||
void WebPlatform::SetThreadPriority(ThreadPriority priority)
|
||||
{
|
||||
// Not supported
|
||||
}
|
||||
|
||||
void WebPlatform::SetThreadAffinityMask(uint64 affinityMask)
|
||||
{
|
||||
// Not supported
|
||||
}
|
||||
|
||||
void WebPlatform::Sleep(int32 milliseconds)
|
||||
{
|
||||
//emscripten_sleep(milliseconds);
|
||||
emscripten_thread_sleep(milliseconds);
|
||||
}
|
||||
|
||||
void WebPlatform::Yield()
|
||||
{
|
||||
Sleep(0);
|
||||
}
|
||||
|
||||
double WebPlatform::GetTimeSeconds()
|
||||
{
|
||||
double time = emscripten_get_now();
|
||||
return time * 0.001;
|
||||
}
|
||||
|
||||
uint64 WebPlatform::GetTimeCycles()
|
||||
{
|
||||
return (uint64)(emscripten_get_now() * 1000.0);
|
||||
}
|
||||
|
||||
void WebPlatform::GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
|
||||
{
|
||||
// Get local time
|
||||
using namespace std::chrono;
|
||||
system_clock::time_point now = system_clock::now();
|
||||
time_t tt = system_clock::to_time_t(now);
|
||||
tm time = *localtime(&tt);
|
||||
|
||||
// Extract time
|
||||
year = time.tm_year + 1900;
|
||||
month = time.tm_mon + 1;
|
||||
dayOfWeek = time.tm_wday;
|
||||
day = time.tm_mday;
|
||||
hour = time.tm_hour;
|
||||
minute = time.tm_min;
|
||||
second = time.tm_sec;
|
||||
millisecond = (int64)emscripten_get_now() % 1000; // Fake it based on other timer
|
||||
}
|
||||
|
||||
void WebPlatform::GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
|
||||
{
|
||||
// Get UTC time
|
||||
using namespace std::chrono;
|
||||
system_clock::time_point now = system_clock::now();
|
||||
time_t tt = system_clock::to_time_t(now);
|
||||
tm time = *gmtime(&tt);
|
||||
|
||||
// Extract time
|
||||
year = time.tm_year + 1900;
|
||||
month = time.tm_mon + 1;
|
||||
dayOfWeek = time.tm_wday;
|
||||
day = time.tm_mday;
|
||||
hour = time.tm_hour;
|
||||
minute = time.tm_min;
|
||||
second = time.tm_sec;
|
||||
millisecond = (int64)emscripten_get_now() % 1000; // Fake it based on other timer
|
||||
}
|
||||
|
||||
#if !BUILD_RELEASE
|
||||
|
||||
void WebPlatform::Log(const StringView& msg)
|
||||
{
|
||||
const StringAsANSI<512> msgAnsi(*msg, msg.Length());
|
||||
|
||||
// Fix % characters that should not be formatted
|
||||
auto buffer = (char*)msgAnsi.Get();
|
||||
for (int32 i = 0; buffer[i]; i++)
|
||||
{
|
||||
if (buffer[i] == '%')
|
||||
buffer[i] = 'p';
|
||||
}
|
||||
|
||||
emscripten_log(EM_LOG_CONSOLE, buffer);
|
||||
}
|
||||
|
||||
bool WebPlatform::IsDebuggerPresent()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
String WebPlatform::GetComputerName()
|
||||
{
|
||||
return TEXT("Web");
|
||||
}
|
||||
|
||||
bool WebPlatform::GetHasFocus()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
String WebPlatform::GetMainDirectory()
|
||||
{
|
||||
return TEXT("/");
|
||||
}
|
||||
|
||||
String WebPlatform::GetExecutableFilePath()
|
||||
{
|
||||
return TEXT("/index.html");
|
||||
}
|
||||
|
||||
Guid WebPlatform::GetUniqueDeviceId()
|
||||
{
|
||||
return Guid(1, 2, 3, 4);
|
||||
}
|
||||
|
||||
String WebPlatform::GetWorkingDirectory()
|
||||
{
|
||||
return GetMainDirectory();
|
||||
}
|
||||
|
||||
bool WebPlatform::SetWorkingDirectory(const String& path)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebPlatform::Init()
|
||||
{
|
||||
if (PlatformBase::Init())
|
||||
return true;
|
||||
|
||||
// Set info about the CPU
|
||||
Platform::MemoryClear(&Cpu, sizeof(Cpu));
|
||||
Cpu.ProcessorPackageCount = 1;
|
||||
Cpu.ProcessorCoreCount = Math::Min(emscripten_num_logical_cores(), PLATFORM_THREADS_LIMIT);
|
||||
Cpu.LogicalProcessorCount = Cpu.ProcessorCoreCount;
|
||||
Cpu.ClockSpeed = GetClockFrequency();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void WebPlatform::LogInfo()
|
||||
{
|
||||
PlatformBase::LogInfo();
|
||||
|
||||
#ifdef __EMSCRIPTEN_major__
|
||||
LOG(Info, "Emscripten {}.{}.{}", __EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__);
|
||||
#elif defined(__EMSCRIPTEN_MAJOR__)
|
||||
LOG(Info, "Emscripten {}.{}.{}", __EMSCRIPTEN_MAJOR__, __EMSCRIPTEN_MINOR__, __EMSCRIPTEN_TINY__);
|
||||
#else
|
||||
LOG(Info, "Emscripten");
|
||||
#endif
|
||||
#ifdef __EMSCRIPTEN_PTHREADS__
|
||||
LOG(Info, "Threading: pthreads");
|
||||
#else
|
||||
LOG(Info, "Threading: disabled");
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebPlatform::Tick()
|
||||
{
|
||||
}
|
||||
|
||||
void WebPlatform::Exit()
|
||||
{
|
||||
}
|
||||
|
||||
void* WebPlatform::LoadLibrary(const Char* filename)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void WebPlatform::FreeLibrary(void* handle)
|
||||
{
|
||||
}
|
||||
|
||||
void* WebPlatform::GetProcAddress(void* handle, const char* symbol)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
116
Source/Engine/Platform/Web/WebPlatform.h
Normal file
116
Source/Engine/Platform/Web/WebPlatform.h
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WEB
|
||||
|
||||
#include "../Unix/UnixPlatform.h"
|
||||
#ifdef __EMSCRIPTEN_PTHREADS__
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The Web platform implementation and application management utilities.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API WebPlatform : public UnixPlatform
|
||||
{
|
||||
public:
|
||||
// [UnixPlatform]
|
||||
FORCE_INLINE static void MemoryBarrier()
|
||||
{
|
||||
#ifdef __EMSCRIPTEN_PTHREADS__
|
||||
// Fake a fence with an arbitrary atomic operation (from emscripten_atomic_fence to avoid including it for less header bloat)
|
||||
uint8 temp = 0;
|
||||
__c11_atomic_fetch_or((_Atomic uint8*)&temp, 0, __ATOMIC_SEQ_CST);
|
||||
#endif
|
||||
}
|
||||
FORCE_INLINE static void MemoryPrefetch(void const* ptr)
|
||||
{
|
||||
__builtin_prefetch(static_cast<char const*>(ptr));
|
||||
}
|
||||
FORCE_INLINE static int64 InterlockedExchange(int64 volatile* dst, int64 exchange)
|
||||
{
|
||||
return __sync_lock_test_and_set(dst, exchange);
|
||||
}
|
||||
FORCE_INLINE static int32 InterlockedCompareExchange(int32 volatile* dst, int32 exchange, int32 comperand)
|
||||
{
|
||||
return __sync_val_compare_and_swap(dst, comperand, exchange);
|
||||
}
|
||||
FORCE_INLINE static int64 InterlockedCompareExchange(int64 volatile* dst, int64 exchange, int64 comperand)
|
||||
{
|
||||
return __sync_val_compare_and_swap(dst, comperand, exchange);
|
||||
}
|
||||
FORCE_INLINE static int64 InterlockedIncrement(int64 volatile* dst)
|
||||
{
|
||||
return __sync_add_and_fetch(dst, 1);
|
||||
}
|
||||
FORCE_INLINE static int64 InterlockedDecrement(int64 volatile* dst)
|
||||
{
|
||||
return __sync_sub_and_fetch(dst, 1);
|
||||
}
|
||||
FORCE_INLINE static int64 InterlockedAdd(int64 volatile* dst, int64 value)
|
||||
{
|
||||
return __sync_fetch_and_add(dst, value);
|
||||
}
|
||||
FORCE_INLINE static int32 AtomicRead(int32 const volatile* dst)
|
||||
{
|
||||
int32 result;
|
||||
__atomic_load(dst, &result, __ATOMIC_SEQ_CST);
|
||||
return result;
|
||||
}
|
||||
FORCE_INLINE static int64 AtomicRead(int64 const volatile* dst)
|
||||
{
|
||||
int64 result;
|
||||
__atomic_load(dst, &result, __ATOMIC_SEQ_CST);
|
||||
return result;
|
||||
}
|
||||
FORCE_INLINE static void AtomicStore(int32 volatile* dst, int32 value)
|
||||
{
|
||||
__atomic_store(dst, &value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
FORCE_INLINE static void AtomicStore(int64 volatile* dst, int64 value)
|
||||
{
|
||||
__atomic_store(dst, &value, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
FORCE_INLINE static uint64 GetCurrentThreadID()
|
||||
{
|
||||
#ifdef __EMSCRIPTEN_PTHREADS__
|
||||
return (uint64)pthread_self();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
static String GetSystemName();
|
||||
static Version GetSystemVersion();
|
||||
static CPUInfo GetCPUInfo();
|
||||
static MemoryStats GetMemoryStats();
|
||||
static ProcessMemoryStats GetProcessMemoryStats();
|
||||
static void SetThreadPriority(ThreadPriority priority);
|
||||
static void SetThreadAffinityMask(uint64 affinityMask);
|
||||
static void Sleep(int32 milliseconds);
|
||||
static void Yield();
|
||||
static double GetTimeSeconds();
|
||||
static uint64 GetTimeCycles();
|
||||
static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
|
||||
static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
|
||||
#if !BUILD_RELEASE
|
||||
static void Log(const StringView& msg);
|
||||
static bool IsDebuggerPresent();
|
||||
#endif
|
||||
static String GetComputerName();
|
||||
static bool GetHasFocus();
|
||||
static String GetMainDirectory();
|
||||
static String GetExecutableFilePath();
|
||||
static Guid GetUniqueDeviceId();
|
||||
static String GetWorkingDirectory();
|
||||
static bool SetWorkingDirectory(const String& path);
|
||||
static bool Init();
|
||||
static void LogInfo();
|
||||
static void Tick();
|
||||
static void Exit();
|
||||
static void* LoadLibrary(const Char* filename);
|
||||
static void FreeLibrary(void* handle);
|
||||
static void* GetProcAddress(void* handle, const char* symbol);
|
||||
};
|
||||
|
||||
#endif
|
||||
27
Source/Engine/Platform/Web/WebPlatformSettings.h
Normal file
27
Source/Engine/Platform/Web/WebPlatformSettings.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WEB || USE_EDITOR
|
||||
|
||||
#include "Engine/Core/Config/PlatformSettingsBase.h"
|
||||
|
||||
/// <summary>
|
||||
/// Web platform settings.
|
||||
/// </summary>
|
||||
API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API WebPlatformSettings : public SettingsBase
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(WebPlatformSettings);
|
||||
API_AUTO_SERIALIZATION();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.
|
||||
/// </summary>
|
||||
static WebPlatformSettings* Get();
|
||||
};
|
||||
|
||||
#if PLATFORM_WEB
|
||||
typedef WebPlatformSettings PlatformSettings;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
63
Source/Engine/Platform/Web/WebThread.h
Normal file
63
Source/Engine/Platform/Web/WebThread.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WEB
|
||||
|
||||
#include "../Base/ThreadBase.h"
|
||||
|
||||
/// <summary>
|
||||
/// Thread object for Web platform.
|
||||
/// </summary>
|
||||
class FLAXENGINE_API WebThread : public ThreadBase
|
||||
{
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebThreadThread"/> class.
|
||||
/// </summary>
|
||||
/// <param name="runnable">The runnable.</param>
|
||||
/// <param name="name">The thread name.</param>
|
||||
/// <param name="priority">The thread priority.</param>
|
||||
WebThread(IRunnable* runnable, const String& name, ThreadPriority priority)
|
||||
: ThreadBase(runnable, name, priority)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Factory method to create a thread with the specified stack size and thread priority
|
||||
/// </summary>
|
||||
/// <param name="runnable">The runnable object to execute</param>
|
||||
/// <param name="name">Name of the thread</param>
|
||||
/// <param name="priority">Tells the thread whether it needs to adjust its priority or not. Defaults to normal priority</param>
|
||||
/// <param name="stackSize">The size of the stack to create. 0 means use the current thread's stack size</param>
|
||||
/// <returns>Pointer to the new thread or null if cannot create it</returns>
|
||||
static WebThread* Create(IRunnable* runnable, const String& name, ThreadPriority priority = ThreadPriority::Normal, uint32 stackSize = 0)
|
||||
{
|
||||
return New<WebThread>(runnable, name, priority);
|
||||
}
|
||||
|
||||
public:
|
||||
// [ThreadBase]
|
||||
void Join() override
|
||||
{
|
||||
// TOOD: impl this
|
||||
}
|
||||
|
||||
protected:
|
||||
// [ThreadBase]
|
||||
void ClearHandleInternal() override
|
||||
{
|
||||
// TOOD: impl this
|
||||
}
|
||||
void SetPriorityInternal(ThreadPriority priority) override
|
||||
{
|
||||
// TOOD: impl this
|
||||
}
|
||||
void KillInternal(bool waitForJoin) override
|
||||
{
|
||||
// TOOD: impl this
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -15,6 +15,8 @@ public class Profiler : EngineModule
|
||||
/// <returns>True if use profiler, otherwise false.</returns>
|
||||
public static bool Use(BuildOptions options)
|
||||
{
|
||||
if (options.Platform.Target == TargetPlatform.Web)
|
||||
return false;
|
||||
return options.Configuration != TargetConfiguration.Release || options.Target.IsEditor;
|
||||
}
|
||||
|
||||
|
||||
@@ -304,7 +304,7 @@ GPUTexture* ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPU
|
||||
// Also for high surface roughness values it adds more blur to the reflection tail which looks more realistic.
|
||||
|
||||
// Downscale with gaussian blur
|
||||
auto filterMode = PLATFORM_ANDROID || PLATFORM_IOS || PLATFORM_SWITCH ? MultiScaler::FilterMode::GaussianBlur5 : MultiScaler::FilterMode::GaussianBlur9;
|
||||
auto filterMode = PLATFORM_WEB || PLATFORM_ANDROID || PLATFORM_IOS || PLATFORM_SWITCH ? MultiScaler::FilterMode::GaussianBlur5 : MultiScaler::FilterMode::GaussianBlur9;
|
||||
for (int32 mipLevel = 1; mipLevel < colorBufferMips; mipLevel++)
|
||||
{
|
||||
const int32 mipWidth = Math::Max(colorBufferWidth >> mipLevel, 1);
|
||||
|
||||
@@ -784,7 +784,7 @@ void ShadowsPass::SetupLight(ShadowsCustomBuffer& shadows, RenderContext& render
|
||||
// Disable cascades blending when baking lightmaps
|
||||
if (IsRunningRadiancePass)
|
||||
atlasLight.BlendCSM = false;
|
||||
#elif PLATFORM_SWITCH || PLATFORM_IOS || PLATFORM_ANDROID
|
||||
#elif PLATFORM_WEB || PLATFORM_SWITCH || PLATFORM_IOS || PLATFORM_ANDROID
|
||||
// Disable cascades blending on low-end platforms
|
||||
atlasLight.BlendCSM = false;
|
||||
#endif
|
||||
|
||||
@@ -6,16 +6,13 @@
|
||||
#include "Engine/Platform/Thread.h"
|
||||
#include "Engine/Platform/ConditionVariable.h"
|
||||
#include "Engine/Core/Types/Span.h"
|
||||
#include "Engine/Core/Types/Pair.h"
|
||||
#include "Engine/Core/Memory/SimpleHeapAllocation.h"
|
||||
#include "Engine/Core/Collections/RingBuffer.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#if USE_CSHARP
|
||||
#include "Engine/Scripting/ManagedCLR/MCore.h"
|
||||
#include "Engine/Scripting/Internal/InternalCalls.h"
|
||||
#endif
|
||||
#include "Engine/Scripting/Internal/InternalCalls.h"
|
||||
|
||||
#define JOB_SYSTEM_ENABLED 1
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ public class TextureTool : EngineModule
|
||||
case TargetPlatform.Switch:
|
||||
case TargetPlatform.Mac:
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.Web:
|
||||
useStb = true;
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
|
||||
@@ -63,6 +63,10 @@ public class Video : EngineModule
|
||||
options.SourcePaths.Add(Path.Combine(FolderPath, "Android"));
|
||||
options.CompileEnv.PreprocessorDefinitions.Add("VIDEO_API_ANDROID");
|
||||
break;
|
||||
case TargetPlatform.Web:
|
||||
// No implemented
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -796,6 +796,7 @@ void ShaderGenerator::ProcessGroupTools(Box* box, Node* node, Value& value)
|
||||
PLATFORM_CASE(10, "PLATFORM_PS5");
|
||||
PLATFORM_CASE(11, "PLATFORM_MAC");
|
||||
PLATFORM_CASE(12, "PLATFORM_IOS");
|
||||
PLATFORM_CASE(13, "PLATFORM_WEB");
|
||||
#undef PLATFORM_CASE
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1015,6 +1015,9 @@ void VisjectExecutor::ProcessGroupTools(Box* box, Node* node, Value& value)
|
||||
case PlatformType::iOS:
|
||||
boxId = 12;
|
||||
break;
|
||||
case PlatformType::Web:
|
||||
boxId = 13;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
value = tryGetValue(node->GetBox(node->GetBox(boxId)->HasConnection() ? boxId : 1), Value::Zero);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "./Flax/ReflectionsCommon.hlsl"
|
||||
|
||||
// Enable/disable blurring SSR during sampling results and mixing with reflections buffer
|
||||
#define SSR_MIX_BLUR (!defined(PLATFORM_ANDROID) && !defined(PLATFORM_IOS) && !defined(PLATFORM_SWITCH))
|
||||
#define SSR_MIX_BLUR (!defined(PLATFORM_WEB) && !defined(PLATFORM_ANDROID) && !defined(PLATFORM_IOS) && !defined(PLATFORM_SWITCH))
|
||||
|
||||
META_CB_BEGIN(0, Data)
|
||||
EnvProbeData PData;
|
||||
|
||||
1
Source/ThirdParty/NvCloth/NvCloth.Build.cs
vendored
1
Source/ThirdParty/NvCloth/NvCloth.Build.cs
vendored
@@ -38,6 +38,7 @@ public class NvCloth : DepsModule
|
||||
case TargetPlatform.Mac:
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.Linux:
|
||||
case TargetPlatform.Web:
|
||||
libName = "NvCloth";
|
||||
break;
|
||||
case TargetPlatform.Switch:
|
||||
|
||||
3
Source/ThirdParty/SDL/SDL.Build.cs
vendored
3
Source/ThirdParty/SDL/SDL.Build.cs
vendored
@@ -48,6 +48,9 @@ public class SDL : DepsModule
|
||||
case TargetPlatform.Mac:
|
||||
options.OutputFiles.Add(Path.Combine(depsRoot, "libSDL3.a"));
|
||||
break;
|
||||
case TargetPlatform.Web:
|
||||
options.OutputFiles.Add("--use-port=sdl3");
|
||||
return;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
|
||||
3
Source/ThirdParty/freetype/freetype.Build.cs
vendored
3
Source/ThirdParty/freetype/freetype.Build.cs
vendored
@@ -45,6 +45,9 @@ public class freetype : DepsModule
|
||||
case TargetPlatform.iOS:
|
||||
options.OutputFiles.Add(Path.Combine(depsRoot, "libfreetype.a"));
|
||||
break;
|
||||
case TargetPlatform.Web:
|
||||
options.OutputFiles.Add("--use-port=freetype");
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
|
||||
|
||||
3
Source/ThirdParty/ogg/ogg.Build.cs
vendored
3
Source/ThirdParty/ogg/ogg.Build.cs
vendored
@@ -45,6 +45,9 @@ public class ogg : DepsModule
|
||||
case TargetPlatform.iOS:
|
||||
options.OutputFiles.Add(Path.Combine(depsRoot, "libogg.a"));
|
||||
break;
|
||||
case TargetPlatform.Web:
|
||||
options.OutputFiles.Add("--use-port=ogg");
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
}
|
||||
|
||||
3
Source/ThirdParty/vorbis/vorbis.Build.cs
vendored
3
Source/ThirdParty/vorbis/vorbis.Build.cs
vendored
@@ -51,6 +51,9 @@ public class vorbis : DepsModule
|
||||
case TargetPlatform.PS5:
|
||||
options.OutputFiles.Add(Path.Combine(depsRoot, "libvorbis.a"));
|
||||
break;
|
||||
case TargetPlatform.Web:
|
||||
options.OutputFiles.Add("--use-port=vorbis");
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace Flax.Build
|
||||
case TargetPlatform.Switch:
|
||||
case TargetPlatform.Mac:
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.Web:
|
||||
options.OutputFiles.Add(Path.Combine(path, string.Format("lib{0}.a", name)));
|
||||
break;
|
||||
default: throw new InvalidPlatformException(options.Platform.Target);
|
||||
|
||||
@@ -108,6 +108,7 @@ namespace Flax.Build
|
||||
case TargetPlatform.Switch: return "PLATFORM_SWITCH";
|
||||
case TargetPlatform.Mac: return "PLATFORM_MAC";
|
||||
case TargetPlatform.iOS: return "PLATFORM_IOS";
|
||||
case TargetPlatform.Web: return "PLATFORM_WEB";
|
||||
default: throw new InvalidPlatformException(platform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +170,7 @@ namespace Flax.Build
|
||||
/// <summary>
|
||||
/// Gets the default project format used by the given platform.
|
||||
/// </summary>
|
||||
public abstract Projects.ProjectFormat DefaultProjectFormat { get; }
|
||||
public virtual Projects.ProjectFormat DefaultProjectFormat => Projects.ProjectFormat.VisualStudioCode;
|
||||
|
||||
/// <summary>
|
||||
/// Creates the toolchain for a given architecture.
|
||||
@@ -343,6 +343,7 @@ namespace Flax.Build
|
||||
case TargetPlatform.Switch: return targetArchitecture == TargetArchitecture.ARM64;
|
||||
case TargetPlatform.Mac: return targetArchitecture == TargetArchitecture.ARM64 || targetArchitecture == TargetArchitecture.x64;
|
||||
case TargetPlatform.iOS: return targetArchitecture == TargetArchitecture.ARM64;
|
||||
case TargetPlatform.Web: return targetArchitecture == TargetArchitecture.x86;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,11 @@ namespace Flax.Build
|
||||
/// Running on iPhone.
|
||||
/// </summary>
|
||||
iOS = 11,
|
||||
|
||||
/// <summary>
|
||||
/// Running on Web.
|
||||
/// </summary>
|
||||
Web = 12,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -296,6 +296,8 @@ namespace Flax.Build
|
||||
|
||||
public static bool WithCSharp(NativeCpp.BuildOptions options)
|
||||
{
|
||||
if (options.Platform.Target == TargetPlatform.Web)
|
||||
return false; // TODO: implement .NET for WebAssembly
|
||||
return UseCSharp || options.Target.IsEditor;
|
||||
}
|
||||
|
||||
@@ -318,6 +320,8 @@ namespace Flax.Build
|
||||
case TargetPlatform.Linux:
|
||||
case TargetPlatform.Mac:
|
||||
return UseSDL;
|
||||
case TargetPlatform.Web:
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace Flax.Build
|
||||
TargetPlatform.Switch,
|
||||
TargetPlatform.Mac,
|
||||
TargetPlatform.iOS,
|
||||
TargetPlatform.Web,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -104,6 +104,7 @@ namespace Flax.Build.Platforms
|
||||
{
|
||||
case TargetPlatform.Linux: return HasRequiredSDKsInstalled;
|
||||
case TargetPlatform.Android: return AndroidSdk.Instance.IsValid && AndroidNdk.Instance.IsValid;
|
||||
case TargetPlatform.Web: return EmscriptenSdk.Instance.IsValid;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace Flax.Build.Platforms
|
||||
{
|
||||
case TargetPlatform.iOS:
|
||||
case TargetPlatform.Mac: return HasRequiredSDKsInstalled;
|
||||
case TargetPlatform.Web: return EmscriptenSdk.Instance.IsValid;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,7 +414,7 @@ namespace Flax.Build.Platforms
|
||||
|
||||
// Language for the file
|
||||
args.Add("-x");
|
||||
if (Path.GetExtension(sourceFile).Equals(".c", StringComparison.OrdinalIgnoreCase))
|
||||
if (sourceFile.EndsWith(".c", StringComparison.OrdinalIgnoreCase))
|
||||
args.Add("c");
|
||||
else
|
||||
{
|
||||
|
||||
87
Source/Tools/Flax.Build/Platforms/Web/EmscriptenSdk.cs
Normal file
87
Source/Tools/Flax.Build/Platforms/Web/EmscriptenSdk.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Flax.Build.Platforms
|
||||
{
|
||||
/// <summary>
|
||||
/// The Emscripten SDK (https://emscripten.org/).
|
||||
/// </summary>
|
||||
/// <seealso cref="Sdk" />
|
||||
public sealed class EmscriptenSdk : Sdk
|
||||
{
|
||||
/// <summary>
|
||||
/// The singleton instance.
|
||||
/// </summary>
|
||||
public static readonly EmscriptenSdk Instance = new EmscriptenSdk();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TargetPlatform[] Platforms => new[]
|
||||
{
|
||||
TargetPlatform.Windows,
|
||||
TargetPlatform.Linux,
|
||||
TargetPlatform.Mac,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Full path to the current SDK folder with binaries, tools and sources (eg. '%EMSDK%\upstream').
|
||||
/// </summary>
|
||||
public string EmscriptenPath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AndroidSdk"/> class.
|
||||
/// </summary>
|
||||
public EmscriptenSdk()
|
||||
{
|
||||
if (!Platforms.Contains(Platform.BuildTargetPlatform))
|
||||
return;
|
||||
|
||||
// Find Emscripten SDK folder path
|
||||
var sdkPath = Environment.GetEnvironmentVariable("EMSDK");
|
||||
if (string.IsNullOrEmpty(sdkPath))
|
||||
{
|
||||
Log.Warning("Missing Emscripten SDK. Cannot build for Web platform.");
|
||||
}
|
||||
else if (!Directory.Exists(sdkPath))
|
||||
{
|
||||
Log.Warning(string.Format("Specified Emscripten SDK folder in EMSDK env variable doesn't exist ({0})", sdkPath));
|
||||
}
|
||||
else
|
||||
{
|
||||
RootPath = sdkPath;
|
||||
EmscriptenPath = Path.Combine(sdkPath, "upstream");
|
||||
var versionPath = Path.Combine(EmscriptenPath, "emscripten", "emscripten-version.txt");
|
||||
if (File.Exists(versionPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Read version
|
||||
var versionStr = File.ReadAllLines(versionPath)[0];
|
||||
versionStr = versionStr.Trim();
|
||||
if (versionStr.StartsWith('\"') && versionStr.EndsWith('\"'))
|
||||
versionStr = versionStr.Substring(1, versionStr.Length - 2);
|
||||
Version = new Version(versionStr);
|
||||
|
||||
var minVersion = new Version(4, 0);
|
||||
if (Version < minVersion)
|
||||
{
|
||||
Log.Error(string.Format("Unsupported Emscripten SDK version {0}. Minimum supported is {1}.", Version, minVersion));
|
||||
return;
|
||||
}
|
||||
Log.Info(string.Format("Found Emscripten SDK {0} at {1}", Version, RootPath));
|
||||
IsValid = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Failed to read Emscripten SDK version from file '{versionPath}'");
|
||||
Log.Exception(ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
Log.Warning($"Missing file {versionPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Source/Tools/Flax.Build/Platforms/Web/WebPlatform.cs
Normal file
68
Source/Tools/Flax.Build/Platforms/Web/WebPlatform.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using Flax.Build.Projects;
|
||||
|
||||
namespace Flax.Build.Platforms
|
||||
{
|
||||
/// <summary>
|
||||
/// The build platform for Web with Emscripten.
|
||||
/// </summary>
|
||||
/// <seealso cref="Platform" />
|
||||
public sealed class WebPlatform : Platform, IProjectCustomizer
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override TargetPlatform Target => TargetPlatform.Web;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasRequiredSDKsInstalled { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasSharedLibrarySupport => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasModularBuildSupport => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool HasDynamicCodeExecutionSupport => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ExecutableFileExtension => ".html";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string SharedLibraryFileExtension => ".so";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string StaticLibraryFileExtension => ".a";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ProgramDatabaseFileExtension => string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Flax.Build.Platforms.WebPlatform"/> class.
|
||||
/// </summary>
|
||||
public WebPlatform()
|
||||
{
|
||||
HasRequiredSDKsInstalled = EmscriptenSdk.Instance.IsValid;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Toolchain CreateToolchain(TargetArchitecture architecture)
|
||||
{
|
||||
if (architecture != TargetArchitecture.x86)
|
||||
throw new InvalidArchitectureException(architecture, "Web is compiled into WebAssembly and doesn't have specific architecture but is mocked as x86.");
|
||||
return new WebToolchain(this, architecture);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IProjectCustomizer.GetSolutionArchitectureName(TargetArchitecture architecture, ref string name)
|
||||
{
|
||||
name = "Web";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
void IProjectCustomizer.GetProjectArchitectureName(Project project, Platform platform, TargetArchitecture architecture, ref string name)
|
||||
{
|
||||
name = "Win32";
|
||||
}
|
||||
}
|
||||
}
|
||||
302
Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs
Normal file
302
Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs
Normal file
@@ -0,0 +1,302 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Flax.Build.Graph;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
namespace Flax.Build.Platforms
|
||||
{
|
||||
/// <summary>
|
||||
/// The build toolchain for Web with Emscripten.
|
||||
/// </summary>
|
||||
/// <seealso cref="Toolchain" />
|
||||
public sealed class WebToolchain : Toolchain
|
||||
{
|
||||
private string _sysrootPath;
|
||||
private string _compilerPath;
|
||||
private Version _compilerVersion;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WebToolchain"/> class.
|
||||
/// </summary>
|
||||
/// <param name="platform">The platform.</param>
|
||||
/// <param name="architecture">The target architecture.</param>
|
||||
public WebToolchain(WebPlatform platform, TargetArchitecture architecture)
|
||||
: base(platform, architecture)
|
||||
{
|
||||
var sdkPath = EmscriptenSdk.Instance.EmscriptenPath;
|
||||
|
||||
// Setup tools
|
||||
_compilerPath = Path.Combine(sdkPath, "emscripten", "emcc");
|
||||
var clangPath = Path.Combine(sdkPath, "bin", "clang++");
|
||||
if (Platform.BuildTargetPlatform == TargetPlatform.Windows)
|
||||
{
|
||||
_compilerPath += ".bat";
|
||||
clangPath += ".exe";
|
||||
}
|
||||
|
||||
// Determinate compiler version
|
||||
_compilerVersion = UnixToolchain.GetClangVersion(platform.Target, clangPath);
|
||||
|
||||
// Setup system paths
|
||||
SystemIncludePaths.Add(Path.Combine(sdkPath, "lib", "clang", _compilerVersion.Major.ToString(), "include"));
|
||||
SystemIncludePaths.Add(Path.Combine(sdkPath, "emscripten", "system", "include"));
|
||||
SystemIncludePaths.Add(Path.Combine(sdkPath, "emscripten", "system", "lib"));
|
||||
_sysrootPath = Path.Combine(sdkPath, "emscripten/cache/sysroot/include");
|
||||
}
|
||||
|
||||
public static string GetLibName(string path)
|
||||
{
|
||||
var libName = Path.GetFileNameWithoutExtension(path);
|
||||
if (libName.StartsWith("lib"))
|
||||
libName = libName.Substring(3);
|
||||
return libName;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DllExport => "__attribute__((__visibility__(\\\"default\\\")))";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string DllImport => "";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TargetCompiler Compiler => TargetCompiler.Clang;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string NativeCompilerPath => _compilerPath;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void LogInfo()
|
||||
{
|
||||
Log.Info("Clang version: " + _compilerVersion);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetupEnvironment(BuildOptions options)
|
||||
{
|
||||
base.SetupEnvironment(options);
|
||||
|
||||
options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_WEB");
|
||||
options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_UNIX");
|
||||
options.CompileEnv.PreprocessorDefinitions.Add("__EMSCRIPTEN__");
|
||||
options.CompileEnv.EnableExceptions = false;
|
||||
options.CompileEnv.CpuArchitecture = CpuArchitecture.None; // TODO: try SIMD support in Emscripten
|
||||
}
|
||||
|
||||
private void AddSharedArgs(List<string> args, bool debugInformation, bool optimization, FavorSizeOrSpeed favorSizeOrSpeed)
|
||||
{
|
||||
if (debugInformation)
|
||||
args.Add("-g2");
|
||||
else
|
||||
args.Add("-g0");
|
||||
|
||||
if (favorSizeOrSpeed == FavorSizeOrSpeed.SmallCode)
|
||||
args.Add("-Os");
|
||||
if (favorSizeOrSpeed == FavorSizeOrSpeed.FastCode)
|
||||
args.Add("-O3");
|
||||
else if (optimization)
|
||||
args.Add("-O2");
|
||||
else
|
||||
args.Add("-O0");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override CompileOutput CompileCppFiles(TaskGraph graph, BuildOptions options, List<string> sourceFiles, string outputPath)
|
||||
{
|
||||
var output = new CompileOutput();
|
||||
|
||||
// Setup arguments shared by all source files
|
||||
var commonArgs = new List<string>();
|
||||
commonArgs.AddRange(options.CompileEnv.CustomArgs);
|
||||
{
|
||||
commonArgs.Add("-c");
|
||||
|
||||
AddSharedArgs(commonArgs, options.CompileEnv.DebugInformation, options.CompileEnv.Optimization, options.CompileEnv.FavorSizeOrSpeed);
|
||||
}
|
||||
|
||||
// Add preprocessor definitions
|
||||
foreach (var definition in options.CompileEnv.PreprocessorDefinitions)
|
||||
{
|
||||
commonArgs.Add(string.Format("-D \"{0}\"", definition));
|
||||
}
|
||||
|
||||
// Add include paths
|
||||
foreach (var includePath in options.CompileEnv.IncludePaths)
|
||||
{
|
||||
if (SystemIncludePaths.Contains(includePath)) // TODO: fix SystemIncludePaths so this chack can be removed
|
||||
continue; // Skip system includes as those break compilation (need to fix sys root linking for emscripten)
|
||||
commonArgs.Add(string.Format("-I\"{0}\"", includePath.Replace('\\', '/')));
|
||||
}
|
||||
|
||||
// Hack for sysroot includes
|
||||
commonArgs.Add(string.Format("-I\"{0}\"", _sysrootPath.Replace('\\', '/')));
|
||||
|
||||
// Compile all C/C++ files
|
||||
var args = new List<string>();
|
||||
foreach (var sourceFile in sourceFiles)
|
||||
{
|
||||
var sourceFilename = Path.GetFileNameWithoutExtension(sourceFile);
|
||||
var task = graph.Add<CompileCppTask>();
|
||||
|
||||
// Use shared arguments
|
||||
args.Clear();
|
||||
args.AddRange(commonArgs);
|
||||
|
||||
// Language for the file
|
||||
args.Add("-x");
|
||||
if (sourceFile.EndsWith(".c", StringComparison.OrdinalIgnoreCase))
|
||||
args.Add("c");
|
||||
else
|
||||
{
|
||||
args.Add("c++");
|
||||
|
||||
// C++ version
|
||||
switch (options.CompileEnv.CppVersion)
|
||||
{
|
||||
case CppVersion.Cpp14:
|
||||
args.Add("-std=c++14");
|
||||
break;
|
||||
case CppVersion.Cpp17:
|
||||
case CppVersion.Latest:
|
||||
args.Add("-std=c++17");
|
||||
break;
|
||||
case CppVersion.Cpp20:
|
||||
args.Add("-std=c++20");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Object File Name
|
||||
var objFile = Path.Combine(outputPath, sourceFilename + ".o");
|
||||
args.Add(string.Format("-o \"{0}\"", objFile.Replace('\\', '/')));
|
||||
output.ObjectFiles.Add(objFile);
|
||||
task.ProducedFiles.Add(objFile);
|
||||
|
||||
// Source File Name
|
||||
args.Add("\"" + sourceFile.Replace('\\', '/') + "\"");
|
||||
|
||||
// Request included files to exist
|
||||
var includes = IncludesCache.FindAllIncludedFiles(sourceFile);
|
||||
task.PrerequisiteFiles.AddRange(includes);
|
||||
|
||||
// Compile
|
||||
task.WorkingDirectory = options.WorkingDirectory;
|
||||
task.CommandPath = _compilerPath;
|
||||
task.CommandArguments = string.Join(" ", args);
|
||||
task.PrerequisiteFiles.Add(sourceFile);
|
||||
task.InfoMessage = Path.GetFileName(sourceFile);
|
||||
task.Cost = task.PrerequisiteFiles.Count; // TODO: include source file size estimation to improve tasks sorting
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
private Task CreateBinary(TaskGraph graph, BuildOptions options, string outputFilePath)
|
||||
{
|
||||
var task = graph.Add<LinkTask>();
|
||||
|
||||
// Setup arguments
|
||||
var args = new List<string>();
|
||||
args.AddRange(options.LinkEnv.CustomArgs);
|
||||
{
|
||||
args.Add(string.Format("-o \"{0}\"", outputFilePath.Replace('\\', '/')));
|
||||
|
||||
AddSharedArgs(args, options.LinkEnv.DebugInformation, options.LinkEnv.Optimization, options.CompileEnv.FavorSizeOrSpeed);
|
||||
}
|
||||
|
||||
args.Add("-Wl,--start-group");
|
||||
|
||||
// Input libraries
|
||||
var libraryPaths = new HashSet<string>();
|
||||
var dynamicLibExt = Platform.SharedLibraryFileExtension;
|
||||
foreach (var library in options.LinkEnv.InputLibraries.Concat(options.Libraries))
|
||||
{
|
||||
var dir = Path.GetDirectoryName(library);
|
||||
var ext = Path.GetExtension(library);
|
||||
if (library.StartsWith("--use-port="))
|
||||
{
|
||||
// Ports (https://emscripten.org/docs/compiling/Building-Projects.html#emscripten-ports)
|
||||
args.Add(library);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(dir))
|
||||
{
|
||||
args.Add(string.Format("\"-l{0}\"", library));
|
||||
}
|
||||
else if (string.IsNullOrEmpty(ext))
|
||||
{
|
||||
// Skip executable
|
||||
}
|
||||
else if (ext == dynamicLibExt)
|
||||
{
|
||||
// Link against dynamic library
|
||||
task.PrerequisiteFiles.Add(library);
|
||||
libraryPaths.Add(dir);
|
||||
args.Add(string.Format("\"-l{0}\"", GetLibName(library)));
|
||||
}
|
||||
else
|
||||
{
|
||||
task.PrerequisiteFiles.Add(library);
|
||||
args.Add(string.Format("\"-l{0}\"", GetLibName(library)));
|
||||
}
|
||||
}
|
||||
|
||||
// Input files (link static libraries last)
|
||||
task.PrerequisiteFiles.AddRange(options.LinkEnv.InputFiles);
|
||||
foreach (var file in options.LinkEnv.InputFiles.Where(x => !x.EndsWith(".a")).Concat(options.LinkEnv.InputFiles.Where(x => x.EndsWith(".a"))))
|
||||
{
|
||||
args.Add(string.Format("\"{0}\"", file.Replace('\\', '/')));
|
||||
}
|
||||
|
||||
// Additional lib paths
|
||||
libraryPaths.AddRange(options.LinkEnv.LibraryPaths);
|
||||
foreach (var path in libraryPaths)
|
||||
{
|
||||
args.Add(string.Format("-L\"{0}\"", path.Replace('\\', '/')));
|
||||
}
|
||||
|
||||
args.Add("-Wl,--end-group");
|
||||
|
||||
// Use a response file (it can contain any commands that you would specify on the command line)
|
||||
bool useResponseFile = true;
|
||||
string responseFile = null;
|
||||
if (useResponseFile)
|
||||
{
|
||||
responseFile = Path.Combine(options.IntermediateFolder, Path.GetFileName(outputFilePath) + ".response");
|
||||
task.PrerequisiteFiles.Add(responseFile);
|
||||
Utilities.WriteFileIfChanged(responseFile, string.Join(Environment.NewLine, args));
|
||||
}
|
||||
|
||||
// Link
|
||||
task.WorkingDirectory = options.WorkingDirectory;
|
||||
task.CommandPath = _compilerPath;
|
||||
task.CommandArguments = useResponseFile ? string.Format("@\"{0}\"", responseFile) : string.Join(" ", args);
|
||||
task.InfoMessage = "Linking " + outputFilePath;
|
||||
task.Cost = task.PrerequisiteFiles.Count;
|
||||
task.ProducedFiles.Add(outputFilePath);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath)
|
||||
{
|
||||
outputFilePath = Utilities.NormalizePath(outputFilePath);
|
||||
|
||||
Task linkTask;
|
||||
switch (options.LinkEnv.Output)
|
||||
{
|
||||
case LinkerOutput.Executable:
|
||||
case LinkerOutput.SharedLibrary:
|
||||
linkTask = CreateBinary(graph, options, outputFilePath);
|
||||
break;
|
||||
case LinkerOutput.StaticLibrary:
|
||||
case LinkerOutput.ImportLibrary:
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ namespace Flax.Build.Platforms
|
||||
case TargetPlatform.Switch: return Sdk.HasValid("SwitchSdk");
|
||||
case TargetPlatform.XboxOne:
|
||||
case TargetPlatform.XboxScarlett: return GetPlatform(platform, true)?.HasRequiredSDKsInstalled ?? false;
|
||||
case TargetPlatform.Web: return EmscriptenSdk.Instance.IsValid;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user