diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp index 6610f2295..8e3e91b51 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp @@ -304,10 +304,13 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data) const Char* gradlew = TEXT("gradlew.bat"); #else const Char* gradlew = TEXT("gradlew"); +#endif +#if PLATFORM_LINUX + Platform::RunProcess(String::Format(TEXT("chmod +x \"{0}/gradlew\""), data.OriginalOutputPath), data.OriginalOutputPath, Dictionary(), true); #endif const bool distributionPackage = buildSettings->ForDistribution; const String gradleCommand = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug")); - const int32 result = Platform::RunProcess(gradleCommand, data.OriginalOutputPath, envVars, true); + const int32 result = Platform::RunProcess(gradleCommand, data.OriginalOutputPath, Dictionary(), true); if (result != 0) { data.Error(TEXT("Failed to build Gradle project into package (result code: {0}). See log for more info."), result); diff --git a/Source/Editor/Editor.Build.cs b/Source/Editor/Editor.Build.cs index 2995aeb1b..200903d88 100644 --- a/Source/Editor/Editor.Build.cs +++ b/Source/Editor/Editor.Build.cs @@ -67,10 +67,12 @@ public class Editor : EditorModule else if (options.Platform.Target == TargetPlatform.Linux) { AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Linux", "PLATFORM_TOOLS_LINUX"); + AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID"); } else if (options.Platform.Target == TargetPlatform.Mac) { AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Mac", "PLATFORM_TOOLS_MAC"); + AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID"); } // Visual Studio integration diff --git a/Source/Editor/GUI/Input/SearchBox.cs b/Source/Editor/GUI/Input/SearchBox.cs index 8ffd0874a..994ef14b1 100644 --- a/Source/Editor/GUI/Input/SearchBox.cs +++ b/Source/Editor/GUI/Input/SearchBox.cs @@ -49,6 +49,12 @@ namespace FlaxEditor.GUI.Input ClearSearchButton.LocalY += 2; ClearSearchButton.LocalX -= 2; ClearSearchButton.Clicked += Clear; + ClearSearchButton.HoverBegin += () => + { + _changeCursor = false; + Cursor = CursorType.Default; + }; + ClearSearchButton.HoverEnd += () => _changeCursor = true; TextChanged += () => ClearSearchButton.Visible = !string.IsNullOrEmpty(Text); } diff --git a/Source/Editor/Windows/GameCookerWindow.cs b/Source/Editor/Windows/GameCookerWindow.cs index 338eebe3e..0c602422b 100644 --- a/Source/Editor/Windows/GameCookerWindow.cs +++ b/Source/Editor/Windows/GameCookerWindow.cs @@ -119,6 +119,7 @@ namespace FlaxEditor.Windows switch (BuildPlatform) { case BuildPlatform.LinuxX64: + case BuildPlatform.AndroidARM64: IsSupported = true; break; default: @@ -129,6 +130,7 @@ namespace FlaxEditor.Windows switch (BuildPlatform) { case BuildPlatform.MacOSx64: + case BuildPlatform.AndroidARM64: IsSupported = true; break; default: diff --git a/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs b/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs index b2780f7ca..252652978 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs +++ b/Source/Engine/GraphicsDevice/Vulkan/GraphicsDeviceVulkan.Build.cs @@ -88,10 +88,12 @@ public sealed class VulkanSdk : Sdk /// /// Tries the get includes folder path (header files). This handles uppercase and lowercase installations for all platforms. /// + /// Target platform hint. /// The includes folder path. /// True if got valid folder, otherwise false. - public bool TryGetIncludePath(out string includesFolderPath) + public bool TryGetIncludePath(TargetPlatform platform, out string includesFolderPath) { + includesFolderPath = string.Empty; if (IsValid) { var vulkanSdk = RootPath; @@ -99,6 +101,11 @@ public sealed class VulkanSdk : Sdk // Use system-installed headers if (vulkanSdk.EndsWith("/include") && Directory.Exists(vulkanSdk)) { + if (platform != Flax.Build.Platform.BuildTargetPlatform) + { + Log.Warning(string.Format("Cannot use system-installed VulkanSDK at {0} when building for platform {1}", vulkanSdk, platform)); + return false; + } includesFolderPath = vulkanSdk; return true; } @@ -123,8 +130,6 @@ public sealed class VulkanSdk : Sdk foreach (var include in includes) Log.Warning(string.Format("No Vulkan header files in {0}", include)); } - - includesFolderPath = string.Empty; return false; } } diff --git a/Source/Engine/Level/Actors/Ragdoll.h b/Source/Engine/Level/Actors/Ragdoll.h index e9571cac5..02206f04f 100644 --- a/Source/Engine/Level/Actors/Ragdoll.h +++ b/Source/Engine/Level/Actors/Ragdoll.h @@ -8,7 +8,7 @@ /// /// Actor that synchronizes Animated Model skeleton pose with physical bones bodies simulated with physics. Child rigidbodies are used for per-bone simulation - rigidbodies names must match skeleton bone name and should be ordered based on importance in the skeleton tree (parents first). /// -API_CLASS() class FLAXENGINE_API Ragdoll : public Actor +API_CLASS(Attributes="ActorToolbox(\"Physics\")") class FLAXENGINE_API Ragdoll : public Actor { DECLARE_SCENE_OBJECT(Ragdoll); API_AUTO_SERIALIZATION(); diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp index a67268b6a..4d81886a8 100644 --- a/Source/Engine/Level/Level.cpp +++ b/Source/Engine/Level/Level.cpp @@ -775,6 +775,14 @@ Actor* FindActorRecursive(Actor* node, const Tag& tag) return result; } +void FindActorsRecursive(Actor* node, const Tag& tag, Array& result) +{ + if (node->HasTag(tag)) + result.Add(node); + for (Actor* child : node->Children) + FindActorsRecursive(child, tag, result); +} + Actor* Level::FindActor(const Tag& tag, Actor* root) { PROFILE_CPU(); @@ -804,12 +812,12 @@ Array Level::FindActors(const Tag& tag, Actor* root) Array result; if (root) { - FindActorRecursive(root, tag); + FindActorsRecursive(root, tag, result); } else { for (Scene* scene : Scenes) - FindActorRecursive(scene, tag); + FindActorsRecursive(scene, tag, result); } return result; } diff --git a/Source/Engine/Platform/Android/AndroidFile.h b/Source/Engine/Platform/Android/AndroidFile.h index 881854637..037d4d62f 100644 --- a/Source/Engine/Platform/Android/AndroidFile.h +++ b/Source/Engine/Platform/Android/AndroidFile.h @@ -11,7 +11,7 @@ typedef struct AAsset AAsset; /// /// Android platform file object implementation. /// -class AndroidFile : public UnixFile +class FLAXENGINE_API AndroidFile : public UnixFile { public: diff --git a/Source/Engine/Platform/Android/AndroidThread.h b/Source/Engine/Platform/Android/AndroidThread.h index e458f8230..336fb000e 100644 --- a/Source/Engine/Platform/Android/AndroidThread.h +++ b/Source/Engine/Platform/Android/AndroidThread.h @@ -10,7 +10,7 @@ /// /// Thread object for Android platform. /// -class AndroidThread : public UnixThread +class FLAXENGINE_API AndroidThread : public UnixThread { public: diff --git a/Source/Engine/Platform/Android/AndroidWindow.h b/Source/Engine/Platform/Android/AndroidWindow.h index d48928dbf..7fca1111d 100644 --- a/Source/Engine/Platform/Android/AndroidWindow.h +++ b/Source/Engine/Platform/Android/AndroidWindow.h @@ -10,7 +10,7 @@ /// /// Implementation of the window class for Android platform. /// -class AndroidWindow : public WindowBase +class FLAXENGINE_API AndroidWindow : public WindowBase { friend AndroidPlatform; public: diff --git a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp index f8ad0d476..8efcba2a4 100644 --- a/Source/Engine/Platform/Linux/LinuxFileSystem.cpp +++ b/Source/Engine/Platform/Linux/LinuxFileSystem.cpp @@ -30,7 +30,6 @@ bool LinuxFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView& const StringAsANSI<> initialDirectoryAnsi(*initialDirectory, initialDirectory.Length()); const StringAsANSI<> titleAnsi(*title, title.Length()); const char* initDir = initialDirectory.HasChars() ? initialDirectoryAnsi.Get() : "."; - char cmd[2048]; String xdgCurrentDesktop; StringBuilder fileFilter; Array fileFilterEntries; @@ -39,6 +38,7 @@ bool LinuxFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView& const bool zenitySupported = FileSystem::FileExists(TEXT("/usr/bin/zenity")); const bool kdialogSupported = FileSystem::FileExists(TEXT("/usr/bin/kdialog")); + char cmd[2048]; if (zenitySupported && (xdgCurrentDesktop != TEXT("KDE") || !kdialogSupported)) // Prefer kdialog when running on KDE { for (int32 i = 1; i < fileFilterEntries.Count(); i += 2) @@ -87,6 +87,51 @@ bool LinuxFileSystem::ShowOpenFileDialog(Window* parentWindow, const StringView& return false; } +bool LinuxFileSystem::ShowBrowseFolderDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& title, String& path) +{ + const StringAsANSI<> titleAnsi(*title, title.Length()); + String xdgCurrentDesktop; + Platform::GetEnvironmentVariable(TEXT("XDG_CURRENT_DESKTOP"), xdgCurrentDesktop); + + // TODO: support initialDirectory + + const bool zenitySupported = FileSystem::FileExists(TEXT("/usr/bin/zenity")); + const bool kdialogSupported = FileSystem::FileExists(TEXT("/usr/bin/kdialog")); + char cmd[2048]; + if (zenitySupported && (xdgCurrentDesktop != TEXT("KDE") || !kdialogSupported)) // Prefer kdialog when running on KDE + { + sprintf(cmd, "/usr/bin/zenity --modal --file-selection --directory --title=\"%s\" ", titleAnsi.Get()); + } + else if (kdialogSupported) + { + sprintf(cmd, "/usr/bin/kdialog --getexistingdirectory --title \"%s\" ", titleAnsi.Get()); + } + else + { + LOG(Error, "Missing file picker (install zenity or kdialog)."); + return true; + } + FILE* f = popen(cmd, "r"); + char buf[2048]; + fgets(buf, ARRAY_COUNT(buf), f); + int result = pclose(f); + if (result != 0) + return true; + + const char* c = buf; + while (*c) + { + const char* start = c; + while (*c != '\n') + c++; + path = String(start, (int32)(c - start)); + if (path.HasChars()) + break; + c++; + } + return false; +} + bool LinuxFileSystem::ShowFileExplorer(const StringView& path) { const StringAsANSI<> pathAnsi(*path, path.Length()); diff --git a/Source/Engine/Platform/Linux/LinuxFileSystem.h b/Source/Engine/Platform/Linux/LinuxFileSystem.h index cce89bcb8..6184d6720 100644 --- a/Source/Engine/Platform/Linux/LinuxFileSystem.h +++ b/Source/Engine/Platform/Linux/LinuxFileSystem.h @@ -15,6 +15,7 @@ public: // [FileSystemBase] static bool ShowOpenFileDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& filter, bool multiSelect, const StringView& title, Array& filenames); + static bool ShowBrowseFolderDialog(Window* parentWindow, const StringView& initialDirectory, const StringView& title, String& path); static bool ShowFileExplorer(const StringView& path); static bool CreateDirectory(const StringView& path); static bool DeleteDirectory(const String& path, bool deleteContents = true); diff --git a/Source/Engine/Platform/Linux/LinuxInput.h b/Source/Engine/Platform/Linux/LinuxInput.h index 2bc7b2312..639f066f7 100644 --- a/Source/Engine/Platform/Linux/LinuxInput.h +++ b/Source/Engine/Platform/Linux/LinuxInput.h @@ -9,7 +9,7 @@ #define LINUXINPUT_MAX_GAMEPAD_EVENTS_PER_FRAME 32 #define TRIGGER_THRESHOLD 1000 -class LinuxGamepad : public Gamepad +class FLAXENGINE_API LinuxGamepad : public Gamepad { public: int fd; diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index 2d04492e6..a6160474e 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -2759,6 +2759,30 @@ Window* LinuxPlatform::CreateWindow(const CreateWindowSettings& settings) return New(settings); } +extern char **environ; + +void LinuxPlatform::GetEnvironmentVariables(Dictionary& result) +{ + char **s = environ; + for (; *s; s++) + { + char* var = *s; + int32 split = -1; + for (int32 i = 0; var[i]; i++) + { + if (var[i] == '=') + { + split = i; + break; + } + } + if (split == -1) + result[String(var)] = String::Empty; + else + result[String(var, split)] = String(var + split + 1); + } +} + bool LinuxPlatform::GetEnvironmentVariable(const String& name, String& value) { char* env = getenv(StringAsANSI<>(*name).Get()); diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.h b/Source/Engine/Platform/Linux/LinuxPlatform.h index e41031f7a..89afe9292 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.h +++ b/Source/Engine/Platform/Linux/LinuxPlatform.h @@ -131,6 +131,7 @@ public: static String GetWorkingDirectory(); static bool SetWorkingDirectory(const String& path); static Window* CreateWindow(const CreateWindowSettings& settings); + static void GetEnvironmentVariables(Dictionary& result); static bool GetEnvironmentVariable(const String& name, String& value); static bool SetEnvironmentVariable(const String& name, const String& value); static int32 StartProcess(const StringView& filename, const StringView& args, const StringView& workingDir, bool hiddenWindow = false, bool waitForEnd = false); diff --git a/Source/Engine/Platform/Linux/LinuxThread.h b/Source/Engine/Platform/Linux/LinuxThread.h index 85778ca9e..53eadf929 100644 --- a/Source/Engine/Platform/Linux/LinuxThread.h +++ b/Source/Engine/Platform/Linux/LinuxThread.h @@ -10,7 +10,7 @@ /// /// Thread object for Linux platform. /// -class LinuxThread : public UnixThread +class FLAXENGINE_API LinuxThread : public UnixThread { public: diff --git a/Source/Engine/Platform/Linux/LinuxWindow.h b/Source/Engine/Platform/Linux/LinuxWindow.h index fb0882cbf..f1a6ee587 100644 --- a/Source/Engine/Platform/Linux/LinuxWindow.h +++ b/Source/Engine/Platform/Linux/LinuxWindow.h @@ -10,7 +10,7 @@ /// /// Implementation of the window class for Linux platform. /// -class LinuxWindow : public WindowBase +class FLAXENGINE_API LinuxWindow : public WindowBase { friend LinuxPlatform; public: diff --git a/Source/Engine/Platform/Mac/MacThread.h b/Source/Engine/Platform/Mac/MacThread.h index 01790d705..f49969628 100644 --- a/Source/Engine/Platform/Mac/MacThread.h +++ b/Source/Engine/Platform/Mac/MacThread.h @@ -10,7 +10,7 @@ /// /// Thread object for Mac platform. /// -class MacThread : public UnixThread +class FLAXENGINE_API MacThread : public UnixThread { public: diff --git a/Source/Engine/Platform/Mac/MacWindow.h b/Source/Engine/Platform/Mac/MacWindow.h index 8498864a9..2a554163c 100644 --- a/Source/Engine/Platform/Mac/MacWindow.h +++ b/Source/Engine/Platform/Mac/MacWindow.h @@ -10,7 +10,7 @@ /// /// Implementation of the window class for Mac platform. /// -class MacWindow : public WindowBase +class FLAXENGINE_API MacWindow : public WindowBase { private: diff --git a/Source/Engine/Platform/UWP/UWPWindow.h b/Source/Engine/Platform/UWP/UWPWindow.h index ebe25bbc7..8e4a62bb8 100644 --- a/Source/Engine/Platform/UWP/UWPWindow.h +++ b/Source/Engine/Platform/UWP/UWPWindow.h @@ -14,7 +14,7 @@ /// /// Implementation of the window class for Universal Windows Platform (UWP) /// -class UWPWindow : public WindowBase +class FLAXENGINE_API UWPWindow : public WindowBase { friend UWPPlatform; diff --git a/Source/Engine/Platform/Unix/UnixFile.h b/Source/Engine/Platform/Unix/UnixFile.h index b36159fea..f09acd30c 100644 --- a/Source/Engine/Platform/Unix/UnixFile.h +++ b/Source/Engine/Platform/Unix/UnixFile.h @@ -9,7 +9,7 @@ /// /// Unix platform file object implementation. /// -class UnixFile : public FileBase +class FLAXENGINE_API UnixFile : public FileBase { protected: diff --git a/Source/Engine/Platform/Unix/UnixThread.h b/Source/Engine/Platform/Unix/UnixThread.h index 8b772ba0b..fdeda63e1 100644 --- a/Source/Engine/Platform/Unix/UnixThread.h +++ b/Source/Engine/Platform/Unix/UnixThread.h @@ -10,7 +10,7 @@ /// /// Thread object for Unix platform. /// -class UnixThread : public ThreadBase +class FLAXENGINE_API UnixThread : public ThreadBase { protected: diff --git a/Source/Engine/Platform/Win32/Win32File.h b/Source/Engine/Platform/Win32/Win32File.h index 3f5685bea..f3a9d7e5d 100644 --- a/Source/Engine/Platform/Win32/Win32File.h +++ b/Source/Engine/Platform/Win32/Win32File.h @@ -9,7 +9,7 @@ /// /// Win32 platform file object implementation /// -class Win32File : public FileBase +class FLAXENGINE_API Win32File : public FileBase { private: diff --git a/Source/Engine/Platform/Win32/Win32Thread.h b/Source/Engine/Platform/Win32/Win32Thread.h index d682d90c4..e758982b8 100644 --- a/Source/Engine/Platform/Win32/Win32Thread.h +++ b/Source/Engine/Platform/Win32/Win32Thread.h @@ -9,7 +9,7 @@ /// /// Thread object for Win32 platform. /// -class Win32Thread : public ThreadBase +class FLAXENGINE_API Win32Thread : public ThreadBase { private: diff --git a/Source/Engine/Platform/Windows/WindowsWindow.h b/Source/Engine/Platform/Windows/WindowsWindow.h index e4c23cbd6..d99cfcb15 100644 --- a/Source/Engine/Platform/Windows/WindowsWindow.h +++ b/Source/Engine/Platform/Windows/WindowsWindow.h @@ -11,7 +11,7 @@ /// /// Implementation of the window class for Windows platform /// -class WindowsWindow : public WindowBase +class FLAXENGINE_API WindowsWindow : public WindowBase #if USE_EDITOR , public Windows::IDropTarget #endif diff --git a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp index 860c1b285..8be3172a6 100644 --- a/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp +++ b/Source/Engine/Renderer/GI/GlobalSurfaceAtlasPass.cpp @@ -149,6 +149,7 @@ public: GlobalSurfaceAtlasTile* AtlasTiles = nullptr; // TODO: optimize with a single allocation for atlas tiles Dictionary Objects; Dictionary Lights; + SamplesBuffer CulledObjectsUsageHistory; // Cached data to be reused during RasterizeActor uint64 CurrentFrame; @@ -167,6 +168,7 @@ public: FORCE_INLINE void ClearObjects() { CulledObjectsCounterIndex = -1; + CulledObjectsUsageHistory.Clear(); LastFrameAtlasDefragmentation = Engine::FrameCount; SAFE_DELETE(AtlasTiles); Objects.Clear(); @@ -717,8 +719,15 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co } } + // Calculate optimal capacity for the objects buffer + objectsBufferCapacity *= sizeof(uint32) * 2; // Convert to bytes and add safe margin + objectsBufferCapacity = Math::Clamp(Math::AlignUp(objectsBufferCapacity, 4096u), 32u * 1024u, 1024u * 1024u); // Align up to 4kB, clamp 32kB - 1MB + surfaceAtlasData.CulledObjectsUsageHistory.Add(objectsBufferCapacity); // Record history + objectsBufferCapacity = surfaceAtlasData.CulledObjectsUsageHistory.Maximum(); // Use biggest value from history + if (surfaceAtlasData.CulledObjectsUsageHistory.Count() == surfaceAtlasData.CulledObjectsUsageHistory.Capacity()) + notReady = false; // Always ready when rendering for some time + // Allocate buffer for culled objects (estimated size) - objectsBufferCapacity = Math::Min(Math::AlignUp(objectsBufferCapacity * sizeof(uint32), 4096u), (uint32)MAX_int32); if (!surfaceAtlasData.CulledObjectsBuffer) surfaceAtlasData.CulledObjectsBuffer = GPUDevice::Instance->CreateBuffer(TEXT("GlobalSurfaceAtlas.CulledObjectsBuffer")); if (surfaceAtlasData.CulledObjectsBuffer->GetSize() < objectsBufferCapacity) @@ -739,7 +748,7 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co data.ViewWorldPos = renderContext.View.Position; data.ViewNearPlane = renderContext.View.Near; data.ViewFarPlane = renderContext.View.Far; - data.CulledObjectsCapacity = objectsBufferCapacity; + data.CulledObjectsCapacity = objectsBufferCapacity / sizeof(uint32); // Capacity in items, not bytes data.GlobalSurfaceAtlas = result.Constants; context->UpdateCB(_cb0, &data); context->BindCB(0, _cb0); diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index 56aa9295f..8dc94deb3 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -460,6 +460,21 @@ TextureTool::PixelFormatSampler PixelFormatSamplers[] = *(Color32*)ptr = Color32(srgb); }, }, + { + PixelFormat::R8G8_UNorm, + sizeof(uint16), + [](const void* ptr) + { + const uint8* rg = (const uint8*)ptr; + return Color((float)rg[0] / MAX_uint8, (float)rg[1] / MAX_uint8, 0, 1); + }, + [](const void* ptr, const Color& color) + { + uint8* rg = (uint8*)ptr; + rg[0] = (uint8)(color.R * MAX_uint8); + rg[1] = (uint8)(color.G * MAX_uint8); + }, + }, { PixelFormat::R16G16_Float, sizeof(Half2), diff --git a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp index 64de58572..86bdd49e1 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.stb.cpp @@ -63,7 +63,21 @@ static TextureData const* stbDecompress(const TextureData& textureData, TextureD { if (!PixelFormatExtensions::IsCompressed(textureData.Format)) return &textureData; - decompressed.Format = PixelFormatExtensions::IsSRGB(textureData.Format) ? PixelFormat::R8G8B8A8_UNorm_sRGB : PixelFormat::R8G8B8A8_UNorm; + const bool srgb = PixelFormatExtensions::IsSRGB(textureData.Format); + switch (textureData.Format) + { + case PixelFormat::BC4_UNorm: + case PixelFormat::BC4_SNorm: + decompressed.Format = PixelFormat::R8_UNorm; + break; + case PixelFormat::BC5_UNorm: + case PixelFormat::BC5_SNorm: + decompressed.Format = PixelFormat::R8G8_UNorm; + break; + default: + decompressed.Format = srgb ? PixelFormat::R8G8B8A8_UNorm_sRGB : PixelFormat::R8G8B8A8_UNorm; + break; + } decompressed.Width = textureData.Width; decompressed.Height = textureData.Height; decompressed.Depth = textureData.Depth; @@ -71,7 +85,7 @@ static TextureData const* stbDecompress(const TextureData& textureData, TextureD decompressed.Items[0].Mips.Resize(1); TextureMipData* decompressedData = decompressed.GetData(0, 0); - decompressedData->RowPitch = textureData.Width * sizeof(Color32); + decompressedData->RowPitch = textureData.Width * PixelFormatExtensions::SizeInBytes(decompressed.Format); decompressedData->Lines = textureData.Height; decompressedData->DepthPitch = decompressedData->RowPitch * decompressedData->Lines; decompressedData->Data.Allocate(decompressedData->DepthPitch); @@ -83,72 +97,74 @@ static TextureData const* stbDecompress(const TextureData& textureData, TextureD const TextureMipData* blocksData = textureData.GetData(0, 0); const byte* blocksBytes = blocksData->Data.Get(); + typedef bool (*detexDecompressBlockFuncType)(const uint8_t* bitstring, uint32_t mode_mask, uint32_t flags, uint8_t* pixel_buffer); + detexDecompressBlockFuncType detexDecompressBlockFunc; + int32 pixelSize, blockSize; switch (textureData.Format) { case PixelFormat::BC1_UNorm: case PixelFormat::BC1_UNorm_sRGB: - { - for (int32 yBlock = 0; yBlock < blocksHeight; yBlock++) - { - for (int32 xBlock = 0; xBlock < blocksWidth; xBlock++) - { - const byte* block = blocksBytes + yBlock * blocksData->RowPitch + xBlock * 8; - detexDecompressBlockBC1(block, 0, 0, (byte*)&colors); - for (int32 y = 0; y < 4; y++) - { - for (int32 x = 0; x < 4; x++) - { - *((Color32*)decompressedBytes + (yBlock * 4 + y) * textureData.Width + (xBlock * 4 + x)) = colors[y * 4 + x]; - } - } - } - } + detexDecompressBlockFunc = detexDecompressBlockBC1; + pixelSize = 4; + blockSize = 8; break; - } case PixelFormat::BC2_UNorm: case PixelFormat::BC2_UNorm_sRGB: - { - for (int32 yBlock = 0; yBlock < blocksHeight; yBlock++) - { - for (int32 xBlock = 0; xBlock < blocksWidth; xBlock++) - { - const byte* block = blocksBytes + yBlock * blocksData->RowPitch + xBlock * 16; - detexDecompressBlockBC2(block, 0, 0, (byte*)&colors); - for (int32 y = 0; y < 4; y++) - { - for (int32 x = 0; x < 4; x++) - { - *((Color32*)decompressedBytes + (yBlock * 4 + y) * textureData.Width + (xBlock * 4 + x)) = colors[y * 4 + x]; - } - } - } - } + detexDecompressBlockFunc = detexDecompressBlockBC2; + pixelSize = 4; + blockSize = 16; break; - } case PixelFormat::BC3_UNorm: case PixelFormat::BC3_UNorm_sRGB: - { - for (int32 yBlock = 0; yBlock < blocksHeight; yBlock++) - { - for (int32 xBlock = 0; xBlock < blocksWidth; xBlock++) - { - const byte* block = blocksBytes + yBlock * blocksData->RowPitch + xBlock * 16; - detexDecompressBlockBC3(block, 0, 0, (byte*)&colors); - for (int32 y = 0; y < 4; y++) - { - for (int32 x = 0; x < 4; x++) - { - *((Color32*)decompressedBytes + (yBlock * 4 + y) * textureData.Width + (xBlock * 4 + x)) = colors[y * 4 + x]; - } - } - } - } + detexDecompressBlockFunc = detexDecompressBlockBC3; + pixelSize = 4; + blockSize = 16; + break; + case PixelFormat::BC4_UNorm: + detexDecompressBlockFunc = detexDecompressBlockRGTC1; + pixelSize = 1; + blockSize = 8; + break; + case PixelFormat::BC5_UNorm: + detexDecompressBlockFunc = detexDecompressBlockRGTC2; + pixelSize = 2; + blockSize = 16; + break; + case PixelFormat::BC7_UNorm: + case PixelFormat::BC7_UNorm_sRGB: + detexDecompressBlockFunc = detexDecompressBlockBPTC; + pixelSize = 4; + blockSize = 16; break; - } default: - LOG(Warning, "Texture data format {0} is not supported by stb library.", (int32)textureData.Format); + LOG(Warning, "Texture data format {0} is not supported by detex library.", (int32)textureData.Format); return nullptr; } + + uint8 blockBuffer[DETEX_MAX_BLOCK_SIZE]; + for (int32 y = 0; y < blocksHeight; y++) + { + int32 rows; + if (y * 4 + 3 >= textureData.Height) + rows = textureData.Height - y * 4; + else + rows = 4; + for (int32 x = 0; x < blocksWidth; x++) + { + const byte* block = blocksBytes + y * blocksData->RowPitch + x * blockSize; + if (!detexDecompressBlockFunc(block, DETEX_MODE_MASK_ALL, 0, blockBuffer)) + memset(blockBuffer, 0, DETEX_MAX_BLOCK_SIZE); + uint8* pixels = decompressedBytes + y * 4 * textureData.Width * pixelSize + x * 4 * pixelSize; + int32 columns; + if (x * 4 + 3 >= textureData.Width) + columns = textureData.Width - x * 4; + else + columns = 4; + for (int32 row = 0; row < rows; row++) + memcpy(pixels + row * textureData.Width * pixelSize, blockBuffer + row * 4 * pixelSize, columns * pixelSize); + } + } + return &decompressed; } diff --git a/Source/Engine/UI/GUI/Common/TextBoxBase.cs b/Source/Engine/UI/GUI/Common/TextBoxBase.cs index 12760c176..fb130bf18 100644 --- a/Source/Engine/UI/GUI/Common/TextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/TextBoxBase.cs @@ -114,6 +114,11 @@ namespace FlaxEngine.GUI /// protected float _animateTime; + /// + /// If the cursor should change to an IBeam + /// + protected bool _changeCursor = true; + /// /// Event fired when text gets changed /// @@ -1131,7 +1136,7 @@ namespace FlaxEngine.GUI /// public override void OnMouseEnter(Float2 location) { - if (_isEditing) + if (_isEditing && _changeCursor) Cursor = CursorType.IBeam; base.OnMouseEnter(location); @@ -1159,8 +1164,8 @@ namespace FlaxEngine.GUI // Modify selection end SetSelection(_selectionStart, currentIndex); } - - if (Cursor == CursorType.Default && _isEditing) + + if (Cursor == CursorType.Default && _isEditing && _changeCursor) Cursor = CursorType.IBeam; } @@ -1191,7 +1196,7 @@ namespace FlaxEngine.GUI SetSelection(hitPos); } - if (Cursor == CursorType.Default) + if (Cursor == CursorType.Default && _changeCursor) Cursor = CursorType.IBeam; return true; @@ -1200,6 +1205,8 @@ namespace FlaxEngine.GUI if (button == MouseButton.Left && !IsFocused) { Focus(); + if (_changeCursor) + Cursor = CursorType.IBeam; return true; } diff --git a/Source/ThirdParty/detex/bits.cpp b/Source/ThirdParty/detex/bits.cpp new file mode 100644 index 000000000..f223f2054 --- /dev/null +++ b/Source/ThirdParty/detex/bits.cpp @@ -0,0 +1,45 @@ +/* + +Copyright (c) 2015 Harm Hanemaaijer + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#include "detex.h" +#include "bits.h" + +uint32_t detexBlock128ExtractBits(detexBlock128 *block, int nu_bits) { + uint32_t value = 0; + for (int i = 0; i < nu_bits; i++) { + if (block->index < 64) { + int shift = block->index - i; + if (shift < 0) + value |= (block->data0 & ((uint64_t)1 << block->index)) << (- shift); + else + value |= (block->data0 & ((uint64_t)1 << block->index)) >> shift; + } + else { + int shift = ((block->index - 64) - i); + if (shift < 0) + value |= (block->data1 & ((uint64_t)1 << (block->index - 64))) << (- shift); + else + value |= (block->data1 & ((uint64_t)1 << (block->index - 64))) >> shift; + } + block->index++; + } +// if (block->index > 128) +// printf("Block overflow (%d)\n", block->index); + return value; +} + diff --git a/Source/ThirdParty/detex/bits.h b/Source/ThirdParty/detex/bits.h new file mode 100644 index 000000000..39350e145 --- /dev/null +++ b/Source/ThirdParty/detex/bits.h @@ -0,0 +1,62 @@ +/* + +Copyright (c) 2015 Harm Hanemaaijer + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +/* Data structure used to extract bits from 128-bit bitstring. */ + +typedef struct { + uint64_t data0; + uint64_t data1; + int index; +} detexBlock128; + +uint32_t detexBlock128ExtractBits(detexBlock128 *block, int nu_bits); + +/* Return bitfield from bit0 to bit1 from 64-bit bitstring. */ +static DETEX_INLINE_ONLY uint32_t detexGetBits64(uint64_t data, int bit0, int bit1) { + return (data & (((uint64_t)1 << (bit1 + 1)) - 1)) >> bit0; +} + +/* Return reversed bitfield (bit1 to bit0) from 64-bit bitstring. */ +static DETEX_INLINE_ONLY uint32_t detexGetBits64Reversed(uint64_t data, int bit0, int bit1) { + // Assumes bit0 > bit1. + // Reverse the bits. + uint32_t val = 0; + for (int i = 0; i <= bit0 - bit1; i++) { + int shift_right = bit0 - 2 * i; + if (shift_right >= 0) + val |= (data & ((uint64_t)1 << (bit0 - i))) >> shift_right; + else + val |= (data & ((uint64_t)1 << (bit0 - i))) << (- shift_right); + } + return val; +} + +/* Clear bit0 to bit1 of 64-bit bitstring. */ +static DETEX_INLINE_ONLY uint64_t detexClearBits64(uint64_t data, int bit0, int bit1) { + uint64_t mask = ~(((uint64_t)1 << (bit1 + 1)) - 1); + mask |= ((uint64_t)1 << bit0) - 1; + return data & mask; +} + +/* Set bit0 to bit1 of 64-bit bitstring. */ +static DETEX_INLINE_ONLY uint64_t detexSetBits64(uint64_t data, int bit0, int bit1, uint64_t val) { + uint64_t d = detexClearBits64(data, bit0, bit1); + d |= val << bit0; + return d; +} + diff --git a/Source/ThirdParty/detex/bptc-tables.cpp b/Source/ThirdParty/detex/bptc-tables.cpp new file mode 100644 index 000000000..ad29a1e89 --- /dev/null +++ b/Source/ThirdParty/detex/bptc-tables.cpp @@ -0,0 +1,203 @@ +/* + +Copyright (c) 2015 Harm Hanemaaijer + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#include "detex.h" +#include "bits.h" +#include "bptc-tables.h" + +const uint8_t detex_bptc_table_P2[64 * 16] = { + 0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1, + 0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1, + 0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1, + 0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1, + 0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1, + 0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1, + 0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1, + 0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1, + 0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1, + 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1, + 0,0,0,0,1,0,0,0,1,1,1,0,1,1,1,1, + 0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0, + 0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0, + 0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0, + 0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0, + 0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0, + 0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1, + 0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0, + 0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0, + 0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0, + 0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0, + 0,0,0,1,0,1,1,1,1,1,1,0,1,0,0,0, + 0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0, + 0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0, + 0,0,1,1,1,0,0,1,1,0,0,1,1,1,0,0, + 0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1, + 0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1, + 0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0, + 0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0, + 0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0, + 0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0, + 0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1, + 0,1,0,1,1,0,1,0,1,0,1,0,0,1,0,1, + 0,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0, + 0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0, + 0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0, + 0,0,1,1,1,0,1,1,1,1,0,1,1,1,0,0, + 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, + 0,0,1,1,1,1,0,0,1,1,0,0,0,0,1,1, + 0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1, + 0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0, + 0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0, + 0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0, + 0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0, + 0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0, + 0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1, + 0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1, + 0,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0, + 0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,0, + 0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,1, + 0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1, + 0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1, + 0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,1, + 0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1, + 0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0, + 0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0, + 0,1,0,0,0,1,0,0,0,1,1,1,0,1,1,1 +}; + +const uint8_t detex_bptc_table_P3[64 * 16] = { + 0,0,1,1,0,0,1,1,0,2,2,1,2,2,2,2, + 0,0,0,1,0,0,1,1,2,2,1,1,2,2,2,1, + 0,0,0,0,2,0,0,1,2,2,1,1,2,2,1,1, + 0,2,2,2,0,0,2,2,0,0,1,1,0,1,1,1, + 0,0,0,0,0,0,0,0,1,1,2,2,1,1,2,2, + 0,0,1,1,0,0,1,1,0,0,2,2,0,0,2,2, + 0,0,2,2,0,0,2,2,1,1,1,1,1,1,1,1, + 0,0,1,1,0,0,1,1,2,2,1,1,2,2,1,1, + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2, + 0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2, + 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2, + 0,0,1,2,0,0,1,2,0,0,1,2,0,0,1,2, + 0,1,1,2,0,1,1,2,0,1,1,2,0,1,1,2, + 0,1,2,2,0,1,2,2,0,1,2,2,0,1,2,2, + 0,0,1,1,0,1,1,2,1,1,2,2,1,2,2,2, + 0,0,1,1,2,0,0,1,2,2,0,0,2,2,2,0, + 0,0,0,1,0,0,1,1,0,1,1,2,1,1,2,2, + 0,1,1,1,0,0,1,1,2,0,0,1,2,2,0,0, + 0,0,0,0,1,1,2,2,1,1,2,2,1,1,2,2, + 0,0,2,2,0,0,2,2,0,0,2,2,1,1,1,1, + 0,1,1,1,0,1,1,1,0,2,2,2,0,2,2,2, + 0,0,0,1,0,0,0,1,2,2,2,1,2,2,2,1, + 0,0,0,0,0,0,1,1,0,1,2,2,0,1,2,2, + 0,0,0,0,1,1,0,0,2,2,1,0,2,2,1,0, + 0,1,2,2,0,1,2,2,0,0,1,1,0,0,0,0, + 0,0,1,2,0,0,1,2,1,1,2,2,2,2,2,2, + 0,1,1,0,1,2,2,1,1,2,2,1,0,1,1,0, + 0,0,0,0,0,1,1,0,1,2,2,1,1,2,2,1, + 0,0,2,2,1,1,0,2,1,1,0,2,0,0,2,2, + 0,1,1,0,0,1,1,0,2,0,0,2,2,2,2,2, + 0,0,1,1,0,1,2,2,0,1,2,2,0,0,1,1, + 0,0,0,0,2,0,0,0,2,2,1,1,2,2,2,1, + 0,0,0,0,0,0,0,2,1,1,2,2,1,2,2,2, + 0,2,2,2,0,0,2,2,0,0,1,2,0,0,1,1, + 0,0,1,1,0,0,1,2,0,0,2,2,0,2,2,2, + 0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,0, + 0,0,0,0,1,1,1,1,2,2,2,2,0,0,0,0, + 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0, + 0,1,2,0,2,0,1,2,1,2,0,1,0,1,2,0, + 0,0,1,1,2,2,0,0,1,1,2,2,0,0,1,1, + 0,0,1,1,1,1,2,2,2,2,0,0,0,0,1,1, + 0,1,0,1,0,1,0,1,2,2,2,2,2,2,2,2, + 0,0,0,0,0,0,0,0,2,1,2,1,2,1,2,1, + 0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2, + 0,0,2,2,0,0,1,1,0,0,2,2,0,0,1,1, + 0,2,2,0,1,2,2,1,0,2,2,0,1,2,2,1, + 0,1,0,1,2,2,2,2,2,2,2,2,0,1,0,1, + 0,0,0,0,2,1,2,1,2,1,2,1,2,1,2,1, + 0,1,0,1,0,1,0,1,0,1,0,1,2,2,2,2, + 0,2,2,2,0,1,1,1,0,2,2,2,0,1,1,1, + 0,0,0,2,1,1,1,2,0,0,0,2,1,1,1,2, + 0,0,0,0,2,1,1,2,2,1,1,2,2,1,1,2, + 0,2,2,2,0,1,1,1,0,1,1,1,0,2,2,2, + 0,0,0,2,1,1,1,2,1,1,1,2,0,0,0,2, + 0,1,1,0,0,1,1,0,0,1,1,0,2,2,2,2, + 0,0,0,0,0,0,0,0,2,1,1,2,2,1,1,2, + 0,1,1,0,0,1,1,0,2,2,2,2,2,2,2,2, + 0,0,2,2,0,0,1,1,0,0,1,1,0,0,2,2, + 0,0,2,2,1,1,2,2,1,1,2,2,0,0,2,2, + 0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,2, + 0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,1, + 0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2, + 0,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2, + 0,1,1,1,2,0,1,1,2,2,0,1,2,2,2,0, +}; + +const uint8_t detex_bptc_table_anchor_index_second_subset[64] = { + 15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15, + 15, 2, 8, 2, 2, 8, 8,15, + 2, 8, 2, 2, 8, 8, 2, 2, + 15,15, 6, 8, 2, 8,15,15, + 2, 8, 2, 2, 2,15,15, 6, + 6, 2, 6, 8,15,15, 2, 2, + 15,15,15,15,15, 2, 2,15 +}; + +const uint8_t detex_bptc_table_anchor_index_second_subset_of_three[64] = { + 3, 3,15,15, 8, 3,15,15, + 8, 8, 6, 6, 6, 5, 3, 3, + 3, 3, 8,15, 3, 3, 6,10, + 5, 8, 8, 6, 8, 5,15,15, + 8,15, 3, 5, 6,10, 8,15, + 15, 3,15, 5,15,15,15,15, + 3,15, 5, 5, 5, 8, 5,10, + 5,10, 8,13,15,12, 3, 3 +}; + +const uint8_t detex_bptc_table_anchor_index_third_subset[64] = { + 15, 8, 8, 3,15,15, 3, 8, + 15,15,15,15,15,15,15, 8, + 15, 8,15, 3,15, 8,15, 8, + 3,15, 6,10,15,15,10, 8, + 15, 3,15,10,10, 8, 9,10, + 6,15, 8,15, 3, 6, 6, 8, + 15, 3,15,15,15,15,15,15, + 15,15,15,15, 3,15,15, 8 +}; + +const uint16_t detex_bptc_table_aWeight2[4] = { + 0, 21, 43, 64 +}; + +const uint16_t detex_bptc_table_aWeight3[8] = { + 0, 9, 18, 27, 37, 46, 55, 64 +}; + +const uint16_t detex_bptc_table_aWeight4[16] = { + 0, 4, 9, 13, 17, 21, 26, 30, + 34, 38, 43, 47, 51, 55, 60, 64 +}; + + diff --git a/Source/ThirdParty/detex/bptc-tables.h b/Source/ThirdParty/detex/bptc-tables.h new file mode 100644 index 000000000..ae11b2af0 --- /dev/null +++ b/Source/ThirdParty/detex/bptc-tables.h @@ -0,0 +1,29 @@ +/* + +Copyright (c) 2015 Harm Hanemaaijer + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +extern const uint8_t detex_bptc_table_P2[64 * 16]; +extern const uint8_t detex_bptc_table_P3[64 * 16]; + +extern const uint8_t detex_bptc_table_anchor_index_second_subset[64]; +extern const uint8_t detex_bptc_table_anchor_index_second_subset_of_three[64]; +extern const uint8_t detex_bptc_table_anchor_index_third_subset[64]; + +extern const uint16_t detex_bptc_table_aWeight2[4]; +extern const uint16_t detex_bptc_table_aWeight3[8]; +extern const uint16_t detex_bptc_table_aWeight4[16]; + diff --git a/Source/ThirdParty/detex/decompress-bptc.cpp b/Source/ThirdParty/detex/decompress-bptc.cpp new file mode 100644 index 000000000..618220561 --- /dev/null +++ b/Source/ThirdParty/detex/decompress-bptc.cpp @@ -0,0 +1,623 @@ +/* + +Copyright (c) 2015 Harm Hanemaaijer + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#include "detex.h" +#include "bits.h" +#include "bptc-tables.h" + +// BPTC mode layout: +// +// Number of subsets = { 3, 2, 3, 2, 1, 1, 1, 2 }; +// Partition bits = { 4, 6, 6, 6, 0, 0, 0, 6 }; +// Rotation bits = { 0, 0, 0, 0, 2, 2, 0, 0 }; +// Mode 4 has one index selection bit. +// +// #subsets color alpha before color index after color index after After Index +// alpha pbits bits (*) +// Mode 0 3 4 0 1 + 4 = 5 5 + 6 * 3 * 4 = 77 77 + 6 = 83 + 48 - 3 = 128 +// Mode 1 2 6 0 2 + 6 = 8 8 + 4 * 3 * 6 = 80 80 + 2 = 82 + 48 - 2 = 128 +// Mode 2 3 5 0 3 + 6 = 9 9 + 6 * 3 * 5 = 99 99 99 + 32 - 3 = 128 +// Mode 3 2 7 0 4 + 6 = 10 10 + 4 * 3 * 7 = 94 94 + 4 = 98 + 32 - 2 = 128 +// Mode 4 1 5 6 5 + 2 + 1 = 8 8 + 2 * 3 * 5 = 38 37 + 2 * 6 = 50 50 + 80 - 2 = 128 +// Mode 5 1 7 8 6 + 2 = 8 8 + 2 * 3 * 7 = 50 50 + 2 * 8 = 66 66 + 64 - 2 = 128 +// Mode 6 1 7 7 7 7 + 2 * 3 * 7 = 49 49 + 2 * 7 = 63 + 2 = 65 + 64 - 1 = 128 +// Mode 7 2 5 5 8 + 6 = 14 14 + 4 * 3 * 5 = 74 74 + 4 * 5 = 94 + 4 = 98 + 32 - 2 = 128 +// +// (*) For formats without alpha, the number of index bits is reduced by #subsets anchor bits. +// For formats with alpha, the number of index bits is reduced by 2 * #subsets by the anchor bits. + + +static const uint8_t color_precision_table[8] = { 4, 6, 5, 7, 5, 7, 7, 5 }; + +// Note: precision includes P-bits! +static const uint8_t color_precision_plus_pbit_table[8] = { 5, 7, 5, 8, 5, 7, 8, 6 }; + +static DETEX_INLINE_ONLY uint8_t GetColorComponentPrecision(int mode) { + return color_precision_table[mode]; +} + +static DETEX_INLINE_ONLY uint8_t GetColorComponentPrecisionPlusPbit(int mode) { + return color_precision_plus_pbit_table[mode]; +} + +static const int8_t alpha_precision_table[8] = { 0, 0, 0, 0, 6, 8, 7, 5 }; + +// Note: precision include P-bits! +static const uint8_t alpha_precision_plus_pbit_table[8] = { 0, 0, 0, 0, 6, 8, 8, 6 }; + +static DETEX_INLINE_ONLY uint8_t GetAlphaComponentPrecision(int mode) { + return alpha_precision_table[mode]; +} + +static DETEX_INLINE_ONLY uint8_t GetAlphaComponentPrecisionPlusPbit(int mode) { + return alpha_precision_plus_pbit_table[mode]; +} + +static const int8_t components_in_qword0_table[8] = { 2, -1, 1, 1, 3, 3, 3, 2 }; + +/* Extract endpoint colors. */ +static void ExtractEndpoints(int mode, int nu_subsets, detexBlock128 * DETEX_RESTRICT block, +uint8_t * DETEX_RESTRICT endpoint_array) { + // Optimized version avoiding the use of block_extract_bits(). + int components_in_qword0 = components_in_qword0_table[mode]; + uint64_t data = block->data0 >> block->index; + uint8_t precision = GetColorComponentPrecision(mode); + uint8_t mask = (1 << precision) - 1; + int total_bits_per_component = nu_subsets * 2 * precision; + for (int i = 0; i < components_in_qword0; i++) // For each color component. + for (int j = 0; j < nu_subsets; j++) // For each subset. + for (int k = 0; k < 2; k++) { // For each endpoint. + endpoint_array[j * 8 + k * 4 + i] = data & mask; + data >>= precision; + } + block->index += components_in_qword0 * total_bits_per_component; + if (components_in_qword0 < 3) { + // Handle the color component that crosses the boundary between data0 and data1 + data = block->data0 >> block->index; + data |= block->data1 << (64 - block->index); + int i = components_in_qword0; + for (int j = 0; j < nu_subsets; j++) // For each subset. + for (int k = 0; k < 2; k++) { // For each endpoint. + endpoint_array[j * 8 + k * 4 + i] = data & mask; + data >>= precision; + } + block->index += total_bits_per_component; + } + if (components_in_qword0 < 2) { + // Handle the color component that is wholly in data1. + data = block->data1 >> (block->index - 64); + int i = 2; + for (int j = 0; j < nu_subsets; j++) // For each subset. + for (int k = 0; k < 2; k++) { // For each endpoint. + endpoint_array[j * 8 + k * 4 + i] = data & mask; + data >>= precision; + } + block->index += total_bits_per_component; + } + // Alpha component. + if (GetAlphaComponentPrecision(mode) > 0) { + // For mode 7, the alpha data is wholly in data1. + // For modes 4 and 6, the alpha data is wholly in data0. + // For mode 5, the alpha data is in data0 and data1. + if (mode == 7) + data = block->data1 >> (block->index - 64); + else if (mode == 5) + data = (block->data0 >> block->index) | ((block->data1 & 0x3) << 14); + else + data = block->data0 >> block->index; + uint8_t alpha_precision = GetAlphaComponentPrecision(mode); + uint8_t mask = (1 << alpha_precision) - 1; + for (int j = 0; j < nu_subsets; j++) + for (int k = 0; k < 2; k++) { // For each endpoint. + endpoint_array[j * 8 + k * 4 + 3] = data & mask; + data >>= alpha_precision; + } + block->index += nu_subsets * 2 * alpha_precision; + } +} + +static const uint8_t mode_has_p_bits[8] = { 1, 1, 0, 1, 0, 0, 1, 1 }; + +static void FullyDecodeEndpoints(uint8_t * DETEX_RESTRICT endpoint_array, int nu_subsets, +int mode, detexBlock128 * DETEX_RESTRICT block) { + if (mode_has_p_bits[mode]) { + // Mode 1 (shared P-bits) handled elsewhere. + // Extract end-point P-bits. Take advantage of the fact that they don't cross the + // 64-bit word boundary in any mode. + uint32_t bits; + if (block->index < 64) + bits = block->data0 >> block->index; + else + bits = block->data1 >> (block->index - 64); + for (int i = 0; i < nu_subsets * 2; i++) { + endpoint_array[i * 4 + 0] <<= 1; + endpoint_array[i * 4 + 1] <<= 1; + endpoint_array[i * 4 + 2] <<= 1; + endpoint_array[i * 4 + 3] <<= 1; + endpoint_array[i * 4 + 0] |= (bits & 1); + endpoint_array[i * 4 + 1] |= (bits & 1); + endpoint_array[i * 4 + 2] |= (bits & 1); + endpoint_array[i * 4 + 3] |= (bits & 1); + bits >>= 1; + } + block->index += nu_subsets * 2; + } + int color_prec = GetColorComponentPrecisionPlusPbit(mode); + int alpha_prec = GetAlphaComponentPrecisionPlusPbit(mode); + for (int i = 0; i < nu_subsets * 2; i++) { + // Color_component_precision & alpha_component_precision includes pbit + // left shift endpoint components so that their MSB lies in bit 7 + endpoint_array[i * 4 + 0] <<= (8 - color_prec); + endpoint_array[i * 4 + 1] <<= (8 - color_prec); + endpoint_array[i * 4 + 2] <<= (8 - color_prec); + endpoint_array[i * 4 + 3] <<= (8 - alpha_prec); + + // Replicate each component's MSB into the LSBs revealed by the left-shift operation above. + endpoint_array[i * 4 + 0] |= (endpoint_array[i * 4 + 0] >> color_prec); + endpoint_array[i * 4 + 1] |= (endpoint_array[i * 4 + 1] >> color_prec); + endpoint_array[i * 4 + 2] |= (endpoint_array[i * 4 + 2] >> color_prec); + endpoint_array[i * 4 + 3] |= (endpoint_array[i * 4 + 3] >> alpha_prec); + } + if (mode <= 3) { + for (int i = 0; i < nu_subsets * 2; i++) + endpoint_array[i * 4 + 3] = 0xFF; + } +} + +static uint8_t Interpolate(uint8_t e0, uint8_t e1, uint8_t index, uint8_t indexprecision) { + if (indexprecision == 2) + return (uint8_t) (((64 - detex_bptc_table_aWeight2[index]) * (uint16_t)e0 + + detex_bptc_table_aWeight2[index] * (uint16_t)e1 + 32) >> 6); + else + if (indexprecision == 3) + return (uint8_t) (((64 - detex_bptc_table_aWeight3[index]) * (uint16_t)e0 + + detex_bptc_table_aWeight3[index] * (uint16_t)e1 + 32) >> 6); + else // indexprecision == 4 + return (uint8_t) (((64 - detex_bptc_table_aWeight4[index]) * (uint16_t)e0 + + detex_bptc_table_aWeight4[index] * (uint16_t)e1 + 32) >> 6); +} + +static const uint8_t bptc_color_index_bitcount[8] = { 3, 3, 2, 2, 2, 2, 4, 2 }; + +static DETEX_INLINE_ONLY int GetColorIndexBitcount(int mode, int index_selection_bit) { + // If the index selection bit is set for mode 4, return 3, otherwise 2. + return bptc_color_index_bitcount[mode] + index_selection_bit; +} + +static uint8_t bptc_alpha_index_bitcount[8] = { 3, 3, 2, 2, 3, 2, 4, 2}; + +static DETEX_INLINE_ONLY int GetAlphaIndexBitcount(int mode, int index_selection_bit) { + // If the index selection bit is set for mode 4, return 2, otherwise 3. + return bptc_alpha_index_bitcount[mode] - index_selection_bit; +} + +static const uint8_t bptc_NS[8] = { 3, 2, 3, 2, 1, 1, 1, 2 }; + +static DETEX_INLINE_ONLY int GetNumberOfSubsets(int mode) { + return bptc_NS[mode]; +} + +static const uint8_t PB[8] = { 4, 6, 6, 6, 0, 0, 0, 6 }; + +static DETEX_INLINE_ONLY int GetNumberOfPartitionBits(int mode) { + return PB[mode]; +} + +static const uint8_t RB[8] = { 0, 0, 0, 0, 2, 2, 0, 0 }; + +static DETEX_INLINE_ONLY int GetNumberOfRotationBits(int mode) { + return RB[mode]; +} + +// Functions to extract parameters. */ + +static int ExtractMode(detexBlock128 *block) { + for (int i = 0; i < 8; i++) + if (block->data0 & ((uint64_t)1 << i)) { + block->index = i + 1; + return i; + } + // Illegal. + return - 1; +} + +static DETEX_INLINE_ONLY int ExtractPartitionSetID(detexBlock128 *block, int mode) { + return detexBlock128ExtractBits(block, GetNumberOfPartitionBits(mode)); +} + +static DETEX_INLINE_ONLY int GetPartitionIndex(int nu_subsets, int partition_set_id, int i) { + if (nu_subsets == 1) + return 0; + if (nu_subsets == 2) + return detex_bptc_table_P2[partition_set_id * 16 + i]; + return detex_bptc_table_P3[partition_set_id * 16 + i]; +} + +static DETEX_INLINE_ONLY int ExtractRotationBits(detexBlock128 *block, int mode) { + return detexBlock128ExtractBits(block, GetNumberOfRotationBits(mode)); +} + +static DETEX_INLINE_ONLY int GetAnchorIndex(int partition_set_id, int partition, int nu_subsets) { + if (partition == 0) + return 0; + if (nu_subsets == 2) + return detex_bptc_table_anchor_index_second_subset[partition_set_id]; + if (partition == 1) + return detex_bptc_table_anchor_index_second_subset_of_three[partition_set_id]; + return detex_bptc_table_anchor_index_third_subset[partition_set_id]; +} + +static const uint8_t IB[8] = { 3, 3, 2, 2, 2, 2, 4, 2 }; +static const uint8_t IB2[8] = { 0, 0, 0, 0, 3, 2, 0, 0 }; +static const uint8_t mode_has_partition_bits[8] = { 1, 1, 1, 1, 0, 0, 0, 1 }; + +/* Decompress a 128-bit 4x4 pixel texture block compressed using BPTC mode 1. */ + +static bool DecompressBlockBPTCMode1(detexBlock128 * DETEX_RESTRICT block, +uint8_t * DETEX_RESTRICT pixel_buffer) { + uint64_t data0 = block->data0; + uint64_t data1 = block->data1; + int partition_set_id = detexGetBits64(data0, 2, 7); + uint8_t endpoint[2 * 2 * 3]; // 2 subsets. + endpoint[0] = detexGetBits64(data0, 8, 13); // red, subset 0, endpoint 0 + endpoint[3] = detexGetBits64(data0, 14, 19); // red, subset 0, endpoint 1 + endpoint[6] = detexGetBits64(data0, 20, 25); // red, subset 1, endpoint 0 + endpoint[9] = detexGetBits64(data0, 26, 31); // red, subset 1, endpoint 1 + endpoint[1] = detexGetBits64(data0, 32, 37); // green, subset 0, endpoint 0 + endpoint[4] = detexGetBits64(data0, 38, 43); // green, subset 0, endpoint 1 + endpoint[7] = detexGetBits64(data0, 44, 49); // green, subset 1, endpoint 0 + endpoint[10] = detexGetBits64(data0, 50, 55); // green, subset 1, endpoint 1 + endpoint[2] = detexGetBits64(data0, 56, 61); // blue, subset 0, endpoint 0 + endpoint[5] = detexGetBits64(data0, 62, 63) // blue, subset 0, endpoint 1 + | (detexGetBits64(data1, 0, 3) << 2); + endpoint[8] = detexGetBits64(data1, 4, 9); // blue, subset 1, endpoint 0 + endpoint[11] = detexGetBits64(data1, 10, 15); // blue, subset 1, endpoint 1 + // Decode endpoints. + for (int i = 0; i < 2 * 2; i++) { + //component-wise left-shift + endpoint[i * 3 + 0] <<= 2; + endpoint[i * 3 + 1] <<= 2; + endpoint[i * 3 + 2] <<= 2; + } + // P-bit is shared. + uint8_t pbit_zero = detexGetBits64(data1, 16, 16) << 1; + uint8_t pbit_one = detexGetBits64(data1, 17, 17) << 1; + // RGB only pbits for mode 1, one for each subset. + for (int j = 0; j < 3; j++) { + endpoint[0 * 3 + j] |= pbit_zero; + endpoint[1 * 3 + j] |= pbit_zero; + endpoint[2 * 3 + j] |= pbit_one; + endpoint[3 * 3 + j] |= pbit_one; + } + for (int i = 0; i < 2 * 2; i++) { + // Replicate each component's MSB into the LSB. + endpoint[i * 3 + 0] |= endpoint[i * 3 + 0] >> 7; + endpoint[i * 3 + 1] |= endpoint[i * 3 + 1] >> 7; + endpoint[i * 3 + 2] |= endpoint[i * 3 + 2] >> 7; + } + + uint8_t subset_index[16]; + for (int i = 0; i < 16; i++) + // subset_index[i] is a number from 0 to 1. + subset_index[i] = detex_bptc_table_P2[partition_set_id * 16 + i]; + uint8_t anchor_index[2]; + anchor_index[0] = 0; + anchor_index[1] = detex_bptc_table_anchor_index_second_subset[partition_set_id]; + uint8_t color_index[16]; + // Extract primary index bits. + data1 >>= 18; + for (int i = 0; i < 16; i++) + if (i == anchor_index[subset_index[i]]) { + // Highest bit is zero. + color_index[i] = data1 & 3; // Get two bits. + data1 >>= 2; + } + else { + color_index[i] = data1 & 7; // Get three bits. + data1 >>= 3; + } + uint32_t *pixel32_buffer = (uint32_t *)pixel_buffer; + for (int i = 0; i < 16; i++) { + uint8_t endpoint_start[3]; + uint8_t endpoint_end[3]; + for (int j = 0; j < 3; j++) { + endpoint_start[j] = endpoint[2 * subset_index[i] * 3 + j]; + endpoint_end[j] = endpoint[(2 * subset_index[i] + 1) * 3 + j]; + } + uint32_t output; + output = detexPack32R8(Interpolate(endpoint_start[0], endpoint_end[0], color_index[i], 3)); + output |= detexPack32G8(Interpolate(endpoint_start[1], endpoint_end[1], color_index[i], 3)); + output |= detexPack32B8(Interpolate(endpoint_start[2], endpoint_end[2], color_index[i], 3)); + output |= detexPack32A8(0xFF); + pixel32_buffer[i] = output; + } + return true; +} + +/* Decompress a 128-bit 4x4 pixel texture block compressed using the BPTC */ +/* (BC7) format. */ +bool detexDecompressBlockBPTC(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask, +uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) { + detexBlock128 block; + block.data0 = *(uint64_t *)&bitstring[0]; + block.data1 = *(uint64_t *)&bitstring[8]; + block.index = 0; + int mode = ExtractMode(&block); + if (mode == - 1) + return 0; + // Allow compression tied to specific modes (according to mode_mask). + if (!(mode_mask & ((int)1 << mode))) + return 0; + if (mode >= 4 && (flags & DETEX_DECOMPRESS_FLAG_OPAQUE_ONLY)) + return 0; + if (mode < 4 && (flags & DETEX_DECOMPRESS_FLAG_NON_OPAQUE_ONLY)) + return 0; + if (mode == 1) + return DecompressBlockBPTCMode1(&block, pixel_buffer); + + int nu_subsets = 1; + int partition_set_id = 0; + if (mode_has_partition_bits[mode]) { + nu_subsets = GetNumberOfSubsets(mode); + partition_set_id = ExtractPartitionSetID(&block, mode); + } + int rotation = ExtractRotationBits(&block, mode); + int index_selection_bit = 0; + if (mode == 4) + index_selection_bit = detexBlock128ExtractBits(&block, 1); + + int alpha_index_bitcount = GetAlphaIndexBitcount(mode, index_selection_bit); + int color_index_bitcount = GetColorIndexBitcount(mode, index_selection_bit); + + uint8_t endpoint_array[3 * 2 * 4]; // Max. 3 subsets. + ExtractEndpoints(mode, nu_subsets, &block, endpoint_array); + FullyDecodeEndpoints(endpoint_array, nu_subsets, mode, &block); + + uint8_t subset_index[16]; + for (int i = 0; i < 16; i++) + // subset_index[i] is a number from 0 to 2, or 0 to 1, or 0 depending on the number of subsets. + subset_index[i] = GetPartitionIndex(nu_subsets, partition_set_id, i); + uint8_t anchor_index[4]; // Only need max. 3 elements. + for (int i = 0; i < nu_subsets; i++) + anchor_index[i] = GetAnchorIndex(partition_set_id, i, nu_subsets); + uint8_t color_index[16]; + uint8_t alpha_index[16]; + // Extract primary index bits. + uint64_t data1; + if (block.index >= 64) { + // Because the index bits are all in the second 64-bit word, there is no need to use + // block_extract_bits(). + // This implies the mode is not 4. + data1 = block.data1 >> (block.index - 64); + uint8_t mask1 = (1 << IB[mode]) - 1; + uint8_t mask2 = (1 << (IB[mode] - 1)) - 1; + for (int i = 0; i < 16; i++) + if (i == anchor_index[subset_index[i]]) { + // Highest bit is zero. + color_index[i] = data1 & mask2; + data1 >>= IB[mode] - 1; + alpha_index[i] = color_index[i]; + } + else { + color_index[i] = data1 & mask1; + data1 >>= IB[mode]; + alpha_index[i] = color_index[i]; + } + } + else { // Implies mode 4. + // Because the bits cross the 64-bit word boundary, we have to be careful. + // Block index is 50 at this point. + uint64_t data = block.data0 >> 50; + data |= block.data1 << 14; + for (int i = 0; i < 16; i++) + if (i == anchor_index[subset_index[i]]) { + // Highest bit is zero. + if (index_selection_bit) { // Implies mode == 4. + alpha_index[i] = data & 0x1; + data >>= 1; + } + else { + color_index[i] = data & 0x1; + data >>= 1; + } + } + else { + if (index_selection_bit) { // Implies mode == 4. + alpha_index[i] = data & 0x3; + data >>= 2; + } + else { + color_index[i] = data & 0x3; + data >>= 2; + } + } + // Block index is 81 at this point. + data1 = block.data1 >> (81 - 64); + } + // Extract secondary index bits. + if (IB2[mode] > 0) { + uint8_t mask1 = (1 << IB2[mode]) - 1; + uint8_t mask2 = (1 << (IB2[mode] - 1)) - 1; + for (int i = 0; i < 16; i++) + if (i == anchor_index[subset_index[i]]) { + // Highest bit is zero. + if (index_selection_bit) { + color_index[i] = data1 & 0x3; + data1 >>= 2; + } + else { +// alpha_index[i] = block_extract_bits(&block, IB2[mode] - 1); + alpha_index[i] = data1 & mask2; + data1 >>= IB2[mode] - 1; + } + } + else { + if (index_selection_bit) { + color_index[i] = data1 & 0x7; + data1 >>= 3; + } + else { +// alpha_index[i] = block_extract_bits(&block, IB2[mode]); + alpha_index[i] = data1 & mask1; + data1 >>= IB2[mode]; + } + } + } + + uint32_t *pixel32_buffer = (uint32_t *)pixel_buffer; + for (int i = 0; i < 16; i++) { + uint8_t endpoint_start[4]; + uint8_t endpoint_end[4]; + for (int j = 0; j < 4; j++) { + endpoint_start[j] = endpoint_array[2 * subset_index[i] * 4 + j]; + endpoint_end[j] = endpoint_array[(2 * subset_index[i] + 1) * 4 + j]; + } + + uint32_t output = 0; + output = detexPack32R8(Interpolate(endpoint_start[0], endpoint_end[0], color_index[i], color_index_bitcount)); + output |= detexPack32G8(Interpolate(endpoint_start[1], endpoint_end[1], color_index[i], color_index_bitcount)); + output |= detexPack32B8(Interpolate(endpoint_start[2], endpoint_end[2], color_index[i], color_index_bitcount)); + output |= detexPack32A8(Interpolate(endpoint_start[3], endpoint_end[3], alpha_index[i], alpha_index_bitcount)); + + if (rotation > 0) { + if (rotation == 1) + output = detexPack32RGBA8(detexPixel32GetA8(output), detexPixel32GetG8(output), + detexPixel32GetB8(output), detexPixel32GetR8(output)); + else + if (rotation == 2) + output = detexPack32RGBA8(detexPixel32GetR8(output), detexPixel32GetA8(output), + detexPixel32GetB8(output), detexPixel32GetG8(output)); + else // rotation == 3 + output = detexPack32RGBA8(detexPixel32GetR8(output), detexPixel32GetG8(output), + detexPixel32GetA8(output), detexPixel32GetB8(output)); + } + pixel32_buffer[i] = output; + } + return true; +} + +#if 0 +/* Modify compressed block to use specific colors. For later use. */ +static void SetBlockColors(uint8_t * DETEX_RESTRICT bitstring, uint32_t flags, +uint32_t * DETEX_RESTRICT colors) { + if ((flags & TWO_COLORS) == 0) + return; + uint64_t data0 = *(uint64_t *)&bitstring[0]; + uint64_t data1 = *(uint64_t *)&bitstring[8]; + if ((flags & BPTC_MODE_ALLOWED_ALL) == (1 << 3)) { + // Mode 3, 7 color bits. + // Color bits at index: 10 + // Color bits end before index: 10 + 4 * 3 * 7 = 94 + uint32_t r0 = detexPixel32GetR8(colors[0]); + uint32_t g0 = detexPixel32GetG8(colors[0]); + uint32_t b0 = detexPixel32GetB8(colors[0]); + uint32_t r1 = detexPixel32GetR8(colors[1]); + uint32_t g1 = detexPixel32GetG8(colors[1]); + uint32_t b1 = detexPixel32GetB8(colors[1]); + data0 = detexSetBits64(data0, 10, 16, r0 >> 1); + data0 = detexSetBits64(data0, 17, 23, r0 >> 1); + data0 = detexSetBits64(data0, 24, 30, r1 >> 1); + data0 = detexSetBits64(data0, 31, 37, r1 >> 1); + data0 = detexSetBits64(data0, 38, 44, g0 >> 1); + data0 = detexSetBits64(data0, 45, 51, g0 >> 1); + data0 = detexSetBits64(data0, 52, 58, g1 >> 1); + data0 = detexSetBits64(data0, 59, 63, (g1 >> 1) & 0x1F); + data1 = detexSetBits64(data1, 0, 1, ((g1 >> 1) & 0x60) >> 5); + data1 = detexSetBits64(data1, 2, 8, b0 >> 1); + data1 = detexSetBits64(data1, 9, 15, b0 >> 1); + data1 = detexSetBits64(data1, 16, 22, b1 >> 1); + data1 = detexSetBits64(data1, 23, 29, b1 >> 1); + *(uint64_t *)&bitstring[0] = data0; + *(uint64_t *)&bitstring[8] = data1; +// printf("bptc_set_block_colors: Colors set for mode 3.\n"); + } + else if ((flags & BPTC_MODE_ALLOWED_ALL) == (1 << 5)) { + // Mode 5, 7 color bits, 8 alpha bits. + // Color bits at index: 6 + 2 = 8 + // Alpha bits at index: 8 + 2 * 3 * 7 = 50 + // Alpha bits end before index: 50 + 2 * 8 = 66 + uint32_t r0 = detexPixel32GetR8(colors[0]); + uint32_t g0 = detexPixel32GetG8(colors[0]); + uint32_t b0 = detexPixel32GetB8(colors[0]); + uint32_t r1 = detexPixel32GetR8(colors[1]); + uint32_t g1 = detexPixel32GetG8(colors[1]); + uint32_t b1 = detexPixel32GetB8(colors[1]); + data0 = detexSetBits64(data0, 8, 14, r0 >> 1); + data0 = detexSetBits64(data0, 15, 21, r1 >> 1); + data0 = detexSetBits64(data0, 22, 28, g0 >> 1); + data0 = detexSetBits64(data0, 29, 35, g0 >> 1); + data0 = detexSetBits64(data0, 36, 42, b0 >> 1); + data0 = detexSetBits64(data0, 43, 49, b1 >> 1); + if (flags & (MODES_ALLOWED_PUNCHTHROUGH_ONLY)) { + data0 = detexSetBits64(data0, 50, 57, 0x00); + data0 = detexSetBits64(data0, 58, 63, 0x3F); + data1 = detexSetBits64(data1, 0, 1, 0x3); + } + *(uint64_t *)&bitstring[0] = data0; + *(uint64_t *)&bitstring[8] = data1; +// printf("bptc_set_block_colors: Colors set for mode 5.\n"); + } + else if ((flags & BPTC_MODE_ALLOWED_ALL) == (1 << 6)) { + // Mode 5, 7 color bits, 7 alpha bits. + // Color bits at index 7. + // Alpha bits at index: 7 + 2 * 3 * 7 = 49 + // Alpha bits end before index: 49 + 2 * 7 = 63 + uint32_t r0 = detexPixel32GetR8(colors[0]); + uint32_t g0 = detexPixel32GetG8(colors[0]); + uint32_t b0 = detexPixel32GetB8(colors[0]); + uint32_t r1 = detexPixel32GetR8(colors[1]); + uint32_t g1 = detexPixel32GetG8(colors[1]); + uint32_t b1 = detexPixel32GetB8(colors[1]); + data0 = detexSetBits64(data0, 7, 13, r0 >> 1); + data0 = detexSetBits64(data0, 14, 20, r1 >> 1); + data0 = detexSetBits64(data0, 21, 27, g0 >> 1); + data0 = detexSetBits64(data0, 28, 34, g1 >> 1); + data0 = detexSetBits64(data0, 35, 41, b0 >> 1); + data0 = detexSetBits64(data0, 42, 48, b1 >> 1); + if (flags & (MODES_ALLOWED_PUNCHTHROUGH_ONLY)) { + data0 = detexSetBits64(data0, 49, 55, 0x00); + data0 = detexSetBits64(data0, 56, 62, 0x7F); + } + *(uint64_t *)&bitstring[0] = data0; +// printf("bptc_set_block_colors: Colors set for mode 6.\n"); + } +} +#endif + +/* Return the internal mode of the BPTC block. */ +uint32_t detexGetModeBPTC(const uint8_t *bitstring) { + detexBlock128 block; + block.data0 = *(uint64_t *)&bitstring[0]; + block.data1 = *(uint64_t *)&bitstring[8]; + block.index = 0; + int mode = ExtractMode(&block); + return mode; +} + +void detexSetModeBPTC(uint8_t *bitstring, uint32_t mode, uint32_t flags, +uint32_t *colors) { + // Mode 0 starts with 1 + // Mode 1 starts with 01 + // ... + // Mode 7 starts with 00000001 + int bit = 0x1 << mode; + bitstring[0] &= ~(bit - 1); + bitstring[0] |= bit; + return; +} + diff --git a/Source/ThirdParty/detex/decompress-rgtc.cpp b/Source/ThirdParty/detex/decompress-rgtc.cpp new file mode 100644 index 000000000..6403e814d --- /dev/null +++ b/Source/ThirdParty/detex/decompress-rgtc.cpp @@ -0,0 +1,148 @@ +/* + +Copyright (c) 2015 Harm Hanemaaijer + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +*/ + +#include "detex.h" + +// For each pixel, decode an 8-bit integer and store as follows: +// If shift and offset are zero, store each value in consecutive 8 bit values in pixel_buffer. +// If shift is one, store each value in consecutive 16-bit words in pixel_buffer; if offset +// is zero, store it in the first 8 bits, if offset is one store it in the last 8 bits of each +// 16-bit word. +static DETEX_INLINE_ONLY void DecodeBlockRGTC(const uint8_t * DETEX_RESTRICT bitstring, int shift, +int offset, uint8_t * DETEX_RESTRICT pixel_buffer) { + // LSBFirst byte order only. + uint64_t bits = (*(uint64_t *)&bitstring[0]) >> 16; + int lum0 = bitstring[0]; + int lum1 = bitstring[1]; + for (int i = 0; i < 16; i++) { + int control_code = bits & 0x7; + uint8_t output; + if (lum0 > lum1) + switch (control_code) { + case 0 : output = lum0; break; + case 1 : output = lum1; break; + case 2 : output = detexDivide0To1791By7(6 * lum0 + lum1); break; + case 3 : output = detexDivide0To1791By7(5 * lum0 + 2 * lum1); break; + case 4 : output = detexDivide0To1791By7(4 * lum0 + 3 * lum1); break; + case 5 : output = detexDivide0To1791By7(3 * lum0 + 4 * lum1); break; + case 6 : output = detexDivide0To1791By7(2 * lum0 + 5 * lum1); break; + case 7 : output = detexDivide0To1791By7(lum0 + 6 * lum1); break; + } + else + switch (control_code) { + case 0 : output = lum0; break; + case 1 : output = lum1; break; + case 2 : output = detexDivide0To1279By5(4 * lum0 + lum1); break; + case 3 : output = detexDivide0To1279By5(3 * lum0 + 2 * lum1); break; + case 4 : output = detexDivide0To1279By5(2 * lum0 + 3 * lum1); break; + case 5 : output = detexDivide0To1279By5(lum0 + 4 * lum1); break; + case 6 : output = 0; break; + case 7 : output = 0xFF; break; + } + pixel_buffer[(i << shift) + offset] = output; + bits >>= 3; + } +} + +/* Decompress a 64-bit 4x4 pixel texture block compressed using the */ +/* unsigned RGTC1 (BC4) format. */ +bool detexDecompressBlockRGTC1(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask, +uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) { + DecodeBlockRGTC(bitstring, 0, 0, pixel_buffer); + return true; +} + +/* Decompress a 128-bit 4x4 pixel texture block compressed using the */ +/* unsigned RGTC2 (BC5) format. */ +bool detexDecompressBlockRGTC2(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask, +uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) { + DecodeBlockRGTC(bitstring, 1, 0, pixel_buffer); + DecodeBlockRGTC(&bitstring[8], 1, 1, pixel_buffer); + return true; +} + +// For each pixel, decode an 16-bit integer and store as follows: +// If shift and offset are zero, store each value in consecutive 16 bit values in pixel_buffer. +// If shift is one, store each value in consecutive 32-bit words in pixel_buffer; if offset +// is zero, store it in the first 16 bits, if offset is one store it in the last 16 bits of each +// 32-bit word. Returns true if the compressed block is valid. +static DETEX_INLINE_ONLY bool DecodeBlockSignedRGTC(const uint8_t * DETEX_RESTRICT bitstring, int shift, +int offset, uint8_t * DETEX_RESTRICT pixel_buffer) { + // LSBFirst byte order only. + uint64_t bits = (*(uint64_t *)&bitstring[0]) >> 16; + int lum0 = (int8_t)bitstring[0]; + int lum1 = (int8_t)bitstring[1]; + if (lum0 == - 127 && lum1 == - 128) + // Not allowed. + return false; + if (lum0 == - 128) + lum0 = - 127; + if (lum1 == - 128) + lum1 = - 127; + // Note: values are mapped to a red value of -127 to 127. + uint16_t *pixel16_buffer = (uint16_t *)pixel_buffer; + for (int i = 0; i < 16; i++) { + int control_code = bits & 0x7; + int32_t result; + if (lum0 > lum1) + switch (control_code) { + case 0 : result = lum0; break; + case 1 : result = lum1; break; + case 2 : result = detexDivideMinus895To895By7(6 * lum0 + lum1); break; + case 3 : result = detexDivideMinus895To895By7(5 * lum0 + 2 * lum1); break; + case 4 : result = detexDivideMinus895To895By7(4 * lum0 + 3 * lum1); break; + case 5 : result = detexDivideMinus895To895By7(3 * lum0 + 4 * lum1); break; + case 6 : result = detexDivideMinus895To895By7(2 * lum0 + 5 * lum1); break; + case 7 : result = detexDivideMinus895To895By7(lum0 + 6 * lum1); break; + } + else + switch (control_code) { + case 0 : result = lum0; break; + case 1 : result = lum1; break; + case 2 : result = detexDivideMinus639To639By5(4 * lum0 + lum1); break; + case 3 : result = detexDivideMinus639To639By5(3 * lum0 + 2 * lum1); break; + case 4 : result = detexDivideMinus639To639By5(2 * lum0 + 3 * lum1); break; + case 5 : result = detexDivideMinus639To639By5(lum0 + 4 * lum1); break; + case 6 : result = - 127; break; + case 7 : result = 127; break; + } + // Map from [-127, 127] to [-32768, 32767]. + pixel16_buffer[(i << shift) + offset] = (uint16_t)(int16_t) + ((result + 127) * 65535 / 254 - 32768); + bits >>= 3; + } + return true; +} + +/* Decompress a 64-bit 4x4 pixel texture block compressed using the */ +/* signed RGTC1 (signed BC4) format. */ +bool detexDecompressBlockSIGNED_RGTC1(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask, +uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) { + return DecodeBlockSignedRGTC(bitstring, 0, 0, pixel_buffer); +} + +/* Decompress a 128-bit 4x4 pixel texture block compressed using the */ +/* signed RGTC2 (signed BC5) format. */ +bool detexDecompressBlockSIGNED_RGTC2(const uint8_t * DETEX_RESTRICT bitstring, uint32_t mode_mask, +uint32_t flags, uint8_t * DETEX_RESTRICT pixel_buffer) { + bool r = DecodeBlockSignedRGTC(bitstring, 1, 0, pixel_buffer); + if (!r) + return false; + return DecodeBlockSignedRGTC(&bitstring[8], 1, 1, pixel_buffer); +} + diff --git a/Source/ThirdParty/detex/detex.h b/Source/ThirdParty/detex/detex.h index c52566679..adbeac512 100644 --- a/Source/ThirdParty/detex/detex.h +++ b/Source/ThirdParty/detex/detex.h @@ -34,7 +34,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define DETEX_HELPER_SHARED_IMPORT __declspec(dllimport) #define DETEX_HELPER_SHARED_EXPORT __declspec(dllexport) #define DETEX_HELPER_SHARED_LOCAL - #define DETEX_INLINE_ONLY __forceinline #else #if __GNUC__ >= 4 #define DETEX_HELPER_SHARED_IMPORT __attribute__ ((visibility ("default"))) @@ -45,7 +44,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define DETEX_HELPER_SHARED_EXPORT #define DETEX_HELPER_SHARED_LOCAL #endif - #define DETEX_INLINE_ONLY __attribute__((always_inline)) inline #endif /* Now we use the generic helper definitions above to define DETEX_API and DETEX_LOCAL. */ @@ -74,6 +72,7 @@ __BEGIN_DECLS #include #include +#define DETEX_INLINE_ONLY __attribute__((always_inline)) inline #define DETEX_RESTRICT __restrict /* Maximum uncompressed block size in bytes. */ diff --git a/Source/ThirdParty/volk/volk.Build.cs b/Source/ThirdParty/volk/volk.Build.cs index 8de605d7e..0b5e4d1c0 100644 --- a/Source/ThirdParty/volk/volk.Build.cs +++ b/Source/ThirdParty/volk/volk.Build.cs @@ -48,7 +48,7 @@ public class volk : ThirdPartyModule } string includesFolderPath; - if (VulkanSdk.Instance.TryGetIncludePath(out includesFolderPath)) + if (VulkanSdk.Instance.TryGetIncludePath(options.Platform.Target, out includesFolderPath)) { options.PublicIncludePaths.Add(includesFolderPath); } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index a4397ef4b..2dddd57d1 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -248,7 +248,7 @@ namespace Flax.Build.Bindings public static void GenerateCppVirtualWrapperCallBaseMethod(BuildData buildData, StringBuilder contents, VirtualClassInfo classInfo, FunctionInfo functionInfo, string scriptVTableBase, string scriptVTableOffset) { contents.AppendLine(" // Prevent stack overflow by calling native base method"); - if (buildData.Toolchain.Compiler == TargetCompiler.Clang) + if (buildData.Toolchain?.Compiler == TargetCompiler.Clang) { // Clang compiler // TODO: secure VTableFunctionInjector with mutex (even at cost of performance) @@ -1616,7 +1616,7 @@ namespace Flax.Build.Bindings } var t = functionInfo.IsConst ? " const" : string.Empty; contents.AppendLine($" typedef {functionInfo.ReturnType} ({classInfo.NativeName}::*{functionInfo.UniqueName}_Signature)({thunkParams}){t};"); - if (buildData.Toolchain.Compiler != TargetCompiler.Clang) + if (buildData.Toolchain?.Compiler != TargetCompiler.Clang) { // MSVC or other compiler contents.AppendLine($" typedef {functionInfo.ReturnType} ({classInfo.NativeName}Internal::*{functionInfo.UniqueName}_Internal_Signature)({thunkParams}){t};"); @@ -1689,7 +1689,7 @@ namespace Flax.Build.Bindings } // Native interfaces override in managed code requires vtables hacking which requires additional inject on Clang-platforms - if (buildData.Toolchain.Compiler == TargetCompiler.Clang && classInfo.IsClass && classInfo.Interfaces != null) + if (buildData.Toolchain?.Compiler == TargetCompiler.Clang && classInfo.IsClass && classInfo.Interfaces != null) { // Override vtable entries of interface methods (for each virtual function in each interface) foreach (var interfaceInfo in classInfo.Interfaces) @@ -2559,7 +2559,7 @@ namespace Flax.Build.Bindings contents.AppendLine(); // Native interfaces override in managed code requires vtables hacking which requires additional inject on Clang-platforms - if (buildData.Toolchain.Compiler == TargetCompiler.Clang) + if (buildData.Toolchain?.Compiler == TargetCompiler.Clang) { // Generate functions that inject script wrappers into vtable entry foreach (var functionInfo in interfaceInfo.Functions) diff --git a/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs b/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs index c82add0ad..2460d2862 100644 --- a/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Linux/LinuxToolchain.cs @@ -28,7 +28,7 @@ namespace Flax.Build.Platforms SystemIncludePaths.Add(includePath); else Log.Error($"Missing toolset header files location {includePath}"); - var cppIncludePath = Path.Combine(includePath, "c++", ClangVersion.ToString()); + var cppIncludePath = Path.Combine(includePath, "c++", LibStdCppVersion); if (Directory.Exists(cppIncludePath)) SystemIncludePaths.Add(cppIncludePath); else diff --git a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs index b137fb650..41fabc840 100644 --- a/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Unix/UnixToolchain.cs @@ -72,6 +72,11 @@ namespace Flax.Build.Platforms /// protected Version ClangVersion; + /// + /// The C++ standard library version. + /// + protected string LibStdCppVersion; + /// /// Initializes a new instance of the class. /// @@ -136,6 +141,7 @@ namespace Flax.Build.Platforms // Determinate compiler version ClangVersion = GetClangVersion(ClangPath); + LibStdCppVersion = GetLibStdCppVersion(ClangPath) ?? ClangVersion.ToString(); // Check version if (ClangVersion.Major < 6) @@ -154,6 +160,31 @@ namespace Flax.Build.Platforms { if (!File.Exists(path)) throw new Exception(string.Format("Missing Clang ({0})", path)); + + // Parse the version + string output = Utilities.ReadProcessOutput(path, "--version -dumpversion"); + Regex versionPattern = new Regex("\\d+(\\.\\d+)+"); + Match versionMatch = versionPattern.Match(output); + if (versionMatch.Success) + { + string[] parts = versionMatch.Value.Split('.'); + int major = 0, minor = 0, patch = 0; + if (parts.Length >= 1) + major = Convert.ToInt32(parts[0]); + if (parts.Length >= 2) + minor = Convert.ToInt32(parts[1]); + if (parts.Length >= 3) + patch = Convert.ToInt32(parts[2]); + return new Version(major, minor, patch); + } + throw new Exception(string.Format("Failed to get Clang version ({0})", path)); + } + + public static string GetLibStdCppVersion(string path) + { + if (!File.Exists(path)) + return null; + using (var process = new Process()) { process.StartInfo.UseShellExecute = false; @@ -161,33 +192,18 @@ namespace Flax.Build.Platforms process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.FileName = path; - process.StartInfo.Arguments = "--version"; + process.StartInfo.Arguments = "-v"; process.Start(); process.WaitForExit(); - if (process.ExitCode == 0) - { - // Parse the version - string data = process.StandardOutput.ReadLine(); - Regex versionPattern = new Regex("version \\d+(\\.\\d+)+"); - Match versionMatch = versionPattern.Match(data); - if (versionMatch.Value.StartsWith("version ")) - { - var versionString = versionMatch.Value.Replace("version ", ""); - string[] parts = versionString.Split('.'); - int major = 0, minor = 0, patch = 0; - if (parts.Length >= 1) - major = Convert.ToInt32(parts[0]); - if (parts.Length >= 2) - minor = Convert.ToInt32(parts[1]); - if (parts.Length >= 3) - patch = Convert.ToInt32(parts[2]); - return new Version(major, minor, patch); - } - } - throw new Exception(string.Format("Failed to get Clang version ({0})", path)); + Regex versionPattern = new Regex("Selected GCC installation: .*\\/(\\d+(\\.\\d+)*)"); + Match versionMatch = versionPattern.Match(process.StandardError.ReadToEnd().Trim()); + if (versionMatch.Success) + return versionMatch.Groups[1].Value; } + + return null; } /// diff --git a/Source/flax.natvis b/Source/flax.natvis index 70f9b15e3..750fd68f5 100644 --- a/Source/flax.natvis +++ b/Source/flax.natvis @@ -217,8 +217,8 @@ - None - Tag={TagsListDebug[Index]} + None + Tag={TagsListDebug[Index - 1]}