diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp index bd187ed1d..d55ad01e3 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.cpp @@ -22,6 +22,11 @@ IMPLEMENT_ENGINE_SETTINGS_GETTER(AndroidPlatformSettings, AndroidPlatform); namespace { + struct AndroidPlatformCache + { + AndroidPlatformSettings::TextureQuality TexturesQuality; + }; + void DeployIcon(const CookingData& data, const TextureData& iconData, const Char* subDir, int32 iconSize, int32 adaptiveIconSize) { const String mipmapPath = data.OriginalOutputPath / TEXT("app/src/main/res") / subDir; @@ -30,6 +35,24 @@ namespace FileSystem::CreateDirectory(mipmapPath); EditorUtilities::ExportApplicationImage(iconData, iconSize, iconSize, PixelFormat::B8G8R8A8_UNorm, iconPath); } + + PixelFormat GetQualityTextureFormat(bool sRGB, PixelFormat format) + { + const auto platformSettings = AndroidPlatformSettings::Get(); + switch (platformSettings->TexturesQuality) + { + case AndroidPlatformSettings::TextureQuality::Uncompressed: + return PixelFormatExtensions::FindUncompressedFormat(format); + case AndroidPlatformSettings::TextureQuality::ASTC_High: + return sRGB ? PixelFormat::ASTC_4x4_UNorm_sRGB : PixelFormat::ASTC_4x4_UNorm; + case AndroidPlatformSettings::TextureQuality::ASTC_Medium: + return sRGB ? PixelFormat::ASTC_6x6_UNorm_sRGB : PixelFormat::ASTC_6x6_UNorm; + case AndroidPlatformSettings::TextureQuality::ASTC_Low: + return sRGB ? PixelFormat::ASTC_8x8_UNorm_sRGB : PixelFormat::ASTC_8x8_UNorm; + default: + return format; + } + } } const Char* AndroidPlatformTools::GetDisplayName() const @@ -54,62 +77,67 @@ ArchitectureType AndroidPlatformTools::GetArchitecture() const PixelFormat AndroidPlatformTools::GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) { - // TODO: add ETC compression support for Android - // TODO: add ASTC compression support for Android - - // BC formats are not widely supported on Android - if (PixelFormatExtensions::IsCompressedBC(format)) - { - switch (format) - { - case PixelFormat::BC1_Typeless: - case PixelFormat::BC2_Typeless: - case PixelFormat::BC3_Typeless: - return PixelFormat::R8G8B8A8_Typeless; - case PixelFormat::BC1_UNorm: - case PixelFormat::BC2_UNorm: - case PixelFormat::BC3_UNorm: - return PixelFormat::R8G8B8A8_UNorm; - case PixelFormat::BC1_UNorm_sRGB: - case PixelFormat::BC2_UNorm_sRGB: - case PixelFormat::BC3_UNorm_sRGB: - return PixelFormat::R8G8B8A8_UNorm_sRGB; - case PixelFormat::BC4_Typeless: - return PixelFormat::R8_Typeless; - case PixelFormat::BC4_UNorm: - return PixelFormat::R8_UNorm; - case PixelFormat::BC4_SNorm: - return PixelFormat::R8_SNorm; - case PixelFormat::BC5_Typeless: - return PixelFormat::R16G16_Typeless; - case PixelFormat::BC5_UNorm: - return PixelFormat::R16G16_UNorm; - case PixelFormat::BC5_SNorm: - return PixelFormat::R16G16_SNorm; - case PixelFormat::BC7_Typeless: - case PixelFormat::BC6H_Typeless: - return PixelFormat::R16G16B16A16_Typeless; - case PixelFormat::BC7_UNorm: - case PixelFormat::BC6H_Uf16: - case PixelFormat::BC6H_Sf16: - return PixelFormat::R16G16B16A16_Float; - case PixelFormat::BC7_UNorm_sRGB: - return PixelFormat::R16G16B16A16_UNorm; - default: - return format; - } - } - switch (format) { - // Not all Android devices support R11G11B10 textures (eg. M6 Note) case PixelFormat::R11G11B10_Float: + // Not all Android devices support R11G11B10 textures (eg. M6 Note) return PixelFormat::R16G16B16A16_UNorm; + case PixelFormat::BC1_Typeless: + case PixelFormat::BC2_Typeless: + case PixelFormat::BC3_Typeless: + case PixelFormat::BC4_Typeless: + case PixelFormat::BC5_Typeless: + case PixelFormat::BC1_UNorm: + case PixelFormat::BC2_UNorm: + case PixelFormat::BC3_UNorm: + case PixelFormat::BC4_UNorm: + case PixelFormat::BC5_UNorm: + return GetQualityTextureFormat(false, format); + case PixelFormat::BC1_UNorm_sRGB: + case PixelFormat::BC2_UNorm_sRGB: + case PixelFormat::BC3_UNorm_sRGB: + case PixelFormat::BC7_UNorm_sRGB: + return GetQualityTextureFormat(true, format); + case PixelFormat::BC4_SNorm: + return PixelFormat::R8_SNorm; + case PixelFormat::BC5_SNorm: + return PixelFormat::R16G16_SNorm; + case PixelFormat::BC6H_Typeless: + case PixelFormat::BC6H_Uf16: + case PixelFormat::BC6H_Sf16: + case PixelFormat::BC7_Typeless: + case PixelFormat::BC7_UNorm: + return PixelFormat::R16G16B16A16_Float; // TODO: ASTC HDR default: return format; } } +void AndroidPlatformTools::LoadCache(CookingData& data, IBuildCache* cache, const Span& bytes) +{ + const auto platformSettings = AndroidPlatformSettings::Get(); + bool invalidTextures = true; + if (bytes.Length() == sizeof(AndroidPlatformCache)) + { + auto* platformCache = (AndroidPlatformCache*)bytes.Get(); + invalidTextures = platformCache->TexturesQuality != platformSettings->TexturesQuality; + } + if (invalidTextures) + { + LOG(Info, "{0} option has been modified.", TEXT("TexturesQuality")); + cache->InvalidateCacheTextures(); + } +} + +Array AndroidPlatformTools::SaveCache(CookingData& data, IBuildCache* cache) +{ + const auto platformSettings = AndroidPlatformSettings::Get(); + AndroidPlatformCache platformCache; + platformCache.TexturesQuality = platformSettings->TexturesQuality; + Array result; + result.Add((const byte*)&platformCache, sizeof(platformCache)); + return result; +} void AndroidPlatformTools::OnBuildStarted(CookingData& data) { // Adjust the cooking output folder to be located inside the Gradle assets directory diff --git a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.h b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.h index 59697e758..e782fb54d 100644 --- a/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.h +++ b/Source/Editor/Cooker/Platform/Android/AndroidPlatformTools.h @@ -30,6 +30,8 @@ public: PlatformType GetPlatform() const override; ArchitectureType GetArchitecture() const override; PixelFormat GetTextureFormat(CookingData& data, TextureBase* texture, PixelFormat format) override; + void LoadCache(CookingData& data, IBuildCache* cache, const Span& bytes) override; + Array SaveCache(CookingData& data, IBuildCache* cache) override; void OnBuildStarted(CookingData& data) override; bool OnPostProcess(CookingData& data) override; }; diff --git a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp index 4d50401bd..78f8fdf1f 100644 --- a/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp +++ b/Source/Editor/Cooker/Platform/iOS/iOSPlatformTools.cpp @@ -66,11 +66,13 @@ namespace return result; } - PixelFormat GetQualityTextureFormat(bool sRGB) + PixelFormat GetQualityTextureFormat(bool sRGB, PixelFormat format) { const auto platformSettings = iOSPlatformSettings::Get(); switch (platformSettings->TexturesQuality) { + case iOSPlatformSettings::TextureQuality::Uncompressed: + return PixelFormatExtensions::FindUncompressedFormat(format); case iOSPlatformSettings::TextureQuality::ASTC_High: return sRGB ? PixelFormat::ASTC_4x4_UNorm_sRGB : PixelFormat::ASTC_4x4_UNorm; case iOSPlatformSettings::TextureQuality::ASTC_Medium: @@ -78,7 +80,7 @@ namespace case iOSPlatformSettings::TextureQuality::ASTC_Low: return sRGB ? PixelFormat::ASTC_8x8_UNorm_sRGB : PixelFormat::ASTC_8x8_UNorm; default: - CRASH; + return format; } } } @@ -122,12 +124,12 @@ PixelFormat iOSPlatformTools::GetTextureFormat(CookingData& data, TextureBase* t case PixelFormat::BC3_UNorm: case PixelFormat::BC4_UNorm: case PixelFormat::BC5_UNorm: - return GetQualityTextureFormat(false); + return GetQualityTextureFormat(false, format); case PixelFormat::BC1_UNorm_sRGB: case PixelFormat::BC2_UNorm_sRGB: case PixelFormat::BC3_UNorm_sRGB: case PixelFormat::BC7_UNorm_sRGB: - return GetQualityTextureFormat(true); + return GetQualityTextureFormat(true, format); case PixelFormat::BC4_SNorm: return PixelFormat::R8_SNorm; case PixelFormat::BC5_SNorm: @@ -137,7 +139,7 @@ PixelFormat iOSPlatformTools::GetTextureFormat(CookingData& data, TextureBase* t case PixelFormat::BC6H_Sf16: case PixelFormat::BC7_Typeless: case PixelFormat::BC7_UNorm: - return PixelFormat::R16G16B16A16_Typeless; // TODO: ASTC HDR + return PixelFormat::R16G16B16A16_Float; // TODO: ASTC HDR default: return format; } diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp index 366a4faef..f53ceae1d 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp @@ -31,6 +31,7 @@ #include "Engine/Graphics/Shaders/GPUShader.h" #include "Engine/Graphics/Textures/TextureData.h" #include "Engine/Graphics/Materials/MaterialShader.h" +#include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Particles/Graph/GPU/ParticleEmitterGraph.GPU.h" #include "Engine/Engine/Base/GameBase.h" #include "Engine/Engine/Globals.h" @@ -643,6 +644,7 @@ bool ProcessTextureBase(CookAssetsStep::AssetCookData& data) const auto& assetHeader = asset->StreamingTexture()->GetHeader(); const auto format = asset->Format(); const auto targetFormat = data.Data.Tools->GetTextureFormat(data.Data, asset, format); + CHECK_RETURN(!PixelFormatExtensions::IsTypeless(targetFormat), true); const auto streamingSettings = StreamingSettings::Get(); int32 mipLevelsMax = GPU_MAX_TEXTURE_MIP_LEVELS; if (assetHeader->TextureGroup >= 0 && assetHeader->TextureGroup < streamingSettings->TextureGroups.Count()) diff --git a/Source/Engine/Graphics/PixelFormatExtensions.cpp b/Source/Engine/Graphics/PixelFormatExtensions.cpp index 2f3aa3931..9d9c21fcd 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.cpp +++ b/Source/Engine/Graphics/PixelFormatExtensions.cpp @@ -915,9 +915,9 @@ PixelFormat PixelFormatExtensions::MakeTypelessUNorm(const PixelFormat format) } } -PixelFormat PixelFormatExtensions::FindShaderResourceFormat(const PixelFormat format, bool isSRGB) +PixelFormat PixelFormatExtensions::FindShaderResourceFormat(const PixelFormat format, bool sRGB) { - if (isSRGB) + if (sRGB) { switch (format) { @@ -1000,3 +1000,55 @@ PixelFormat PixelFormatExtensions::FindDepthStencilFormat(const PixelFormat form } return format; } + +PixelFormat PixelFormatExtensions::FindUncompressedFormat(PixelFormat format) +{ + switch (format) + { + case PixelFormat::BC1_Typeless: + case PixelFormat::BC2_Typeless: + case PixelFormat::BC3_Typeless: + return PixelFormat::R8G8B8A8_Typeless; + case PixelFormat::BC1_UNorm: + case PixelFormat::BC2_UNorm: + case PixelFormat::BC3_UNorm: + return PixelFormat::R8G8B8A8_UNorm; + case PixelFormat::BC1_UNorm_sRGB: + case PixelFormat::BC2_UNorm_sRGB: + case PixelFormat::BC3_UNorm_sRGB: + return PixelFormat::R8G8B8A8_UNorm_sRGB; + case PixelFormat::BC4_Typeless: + return PixelFormat::R8_Typeless; + case PixelFormat::BC4_UNorm: + return PixelFormat::R8_UNorm; + case PixelFormat::BC4_SNorm: + return PixelFormat::R8_SNorm; + case PixelFormat::BC5_Typeless: + return PixelFormat::R16G16_Typeless; + case PixelFormat::BC5_UNorm: + return PixelFormat::R16G16_UNorm; + case PixelFormat::BC5_SNorm: + return PixelFormat::R16G16_SNorm; + case PixelFormat::BC7_Typeless: + case PixelFormat::BC6H_Typeless: + return PixelFormat::R16G16B16A16_Typeless; + case PixelFormat::BC7_UNorm: + case PixelFormat::BC6H_Uf16: + case PixelFormat::BC6H_Sf16: + return PixelFormat::R16G16B16A16_Float; + case PixelFormat::BC7_UNorm_sRGB: + return PixelFormat::R16G16B16A16_UNorm; + case PixelFormat::ASTC_4x4_UNorm: + case PixelFormat::ASTC_6x6_UNorm: + case PixelFormat::ASTC_8x8_UNorm: + case PixelFormat::ASTC_10x10_UNorm: + return PixelFormat::R8G8B8A8_UNorm; + case PixelFormat::ASTC_4x4_UNorm_sRGB: + case PixelFormat::ASTC_6x6_UNorm_sRGB: + case PixelFormat::ASTC_8x8_UNorm_sRGB: + case PixelFormat::ASTC_10x10_UNorm_sRGB: + return PixelFormat::R8G8B8A8_UNorm_sRGB; + default: + return format; + } +} diff --git a/Source/Engine/Graphics/PixelFormatExtensions.h b/Source/Engine/Graphics/PixelFormatExtensions.h index 7b793cdfd..7ba460e57 100644 --- a/Source/Engine/Graphics/PixelFormatExtensions.h +++ b/Source/Engine/Graphics/PixelFormatExtensions.h @@ -72,7 +72,7 @@ public: /// The . /// Enable/disable partially typeless formats. /// true if the specified is Typeless; otherwise, false. - API_FUNCTION() static bool IsTypeless(PixelFormat format, bool partialTypeless); + API_FUNCTION() static bool IsTypeless(PixelFormat format, bool partialTypeless = true); /// /// Returns true if the is valid. @@ -215,7 +215,8 @@ public: API_FUNCTION() static PixelFormat MakeTypelessUNorm(PixelFormat format); public: - static PixelFormat FindShaderResourceFormat(PixelFormat format, bool bSRGB); + static PixelFormat FindShaderResourceFormat(PixelFormat format, bool sRGB); static PixelFormat FindUnorderedAccessFormat(PixelFormat format); static PixelFormat FindDepthStencilFormat(PixelFormat format); + static PixelFormat FindUncompressedFormat(PixelFormat format); }; diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index ea70a9d9e..bb4273826 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -297,7 +297,7 @@ Task* StreamingTexture::UpdateAllocation(int32 residency) if (texture->Init(desc)) { Streaming.Error = true; - LOG(Error, "Cannot allocate texture {0}.", ToString()); + LOG(Error, "Cannot allocate texture {0}.", texture->GetName()); } if (allocatedResidency != 0) { diff --git a/Source/Engine/Platform/Android/AndroidPlatformSettings.h b/Source/Engine/Platform/Android/AndroidPlatformSettings.h index 974a4c488..08d63b2ef 100644 --- a/Source/Engine/Platform/Android/AndroidPlatformSettings.h +++ b/Source/Engine/Platform/Android/AndroidPlatformSettings.h @@ -48,6 +48,24 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API AutoRotation, }; + /// + /// The output textures quality (compression). + /// + API_ENUM() enum class TextureQuality + { + // Raw image data without any compression algorithm. Mostly for testing or compatibility. + Uncompressed, + // ASTC 4x4 block compression. + API_ENUM(Attributes="EditorDisplay(null, \"ASTC High\")") + ASTC_High, + // ASTC 6x6 block compression. + API_ENUM(Attributes="EditorDisplay(null, \"ASTC Medium\")") + ASTC_Medium, + // ASTC 8x8 block compression. + API_ENUM(Attributes="EditorDisplay(null, \"ASTC Low\")") + ASTC_Low, + }; + /// /// The application package name (eg. com.company.product). Custom tokens: ${PROJECT_NAME}, ${COMPANY_NAME}. /// @@ -66,6 +84,12 @@ API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API API_FIELD(Attributes = "EditorOrder(110), EditorDisplay(\"General\")") ScreenOrientation DefaultOrientation = ScreenOrientation::AutoRotation; + /// + /// The output textures quality (compression). + /// + API_FIELD(Attributes="EditorOrder(500), EditorDisplay(\"General\")") + TextureQuality TexturesQuality = TextureQuality::ASTC_Medium; + /// /// Custom icon texture to use for the application (overrides the default one). /// diff --git a/Source/Engine/Platform/iOS/iOSPlatformSettings.h b/Source/Engine/Platform/iOS/iOSPlatformSettings.h index 4016f2767..ab381fdb9 100644 --- a/Source/Engine/Platform/iOS/iOSPlatformSettings.h +++ b/Source/Engine/Platform/iOS/iOSPlatformSettings.h @@ -51,15 +51,17 @@ API_CLASS(Sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API /// API_ENUM() enum class TextureQuality { + // Raw image data without any compression algorithm. Mostly for testing or compatibility. + Uncompressed, // ASTC 4x4 block compression. API_ENUM(Attributes="EditorDisplay(null, \"ASTC High\")") - ASTC_High = 0, + ASTC_High, // ASTC 6x6 block compression. API_ENUM(Attributes="EditorDisplay(null, \"ASTC Medium\")") - ASTC_Medium = 1, + ASTC_Medium, // ASTC 8x8 block compression. API_ENUM(Attributes="EditorDisplay(null, \"ASTC Low\")") - ASTC_Low = 2, + ASTC_Low, }; ///