Add **Web platform with Emscripten**

This commit is contained in:
Wojtek Figat
2026-02-14 00:07:21 +01:00
parent fd0584b406
commit f12ad5c874
80 changed files with 1529 additions and 61 deletions

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -90,6 +90,7 @@ public:
Guid PS5Platform;
Guid MacPlatform;
Guid iOSPlatform;
Guid WebPlatform;
public:
/// <summary>

View File

@@ -38,3 +38,6 @@
#if PLATFORM_IOS
#include "Engine/Platform/iOS/iOSPlatformSettings.h"
#endif
#if PLATFORM_WEB
#include "Engine/Platform/Web/WebPlatformSettings.h"
#endif

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}
}

View 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

View File

@@ -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)

View File

@@ -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();

View File

@@ -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("");
}

View File

@@ -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.

View File

@@ -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)

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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"));

View File

@@ -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

View File

@@ -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"

View 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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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!

View 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

View 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

View 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

View 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

View 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

View 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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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);