diff --git a/engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.cc index 67f73eff4e0..9bc8da85383 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.cc +++ b/engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.cc @@ -219,6 +219,10 @@ bool CapabilitiesGLES::SupportsPrimitiveRestart() const { return false; } +bool CapabilitiesGLES::SupportsExtendedRangeFormats() const { + return false; +} + PixelFormat CapabilitiesGLES::GetDefaultGlyphAtlasFormat() const { return default_glyph_atlas_format_; } diff --git a/engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.h b/engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.h index fa39cfecab6..8783aa46989 100644 --- a/engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.h +++ b/engine/src/flutter/impeller/renderer/backend/gles/capabilities_gles.h @@ -116,6 +116,9 @@ class CapabilitiesGLES final // |Capabilities| bool SupportsPrimitiveRestart() const override; + // |Capabilities| + bool SupportsExtendedRangeFormats() const override; + // |Capabilities| PixelFormat GetDefaultColorFormat() const override; diff --git a/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.mm b/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.mm index a4083bfa63e..7421a330bb9 100644 --- a/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.mm +++ b/engine/src/flutter/impeller/renderer/backend/metal/context_mtl.mm @@ -52,6 +52,15 @@ static bool DeviceSupportsComputeSubgroups(id device) { return supports_subgroups; } +// See "Extended Range and wide color pixel formats" in the metal feature set +// tables. +static bool DeviceSupportsExtendedRangeFormats(id device) { + if (@available(macOS 10.15, iOS 13, tvOS 13, *)) { + return [device supportsFamily:MTLGPUFamilyApple3]; + } + return false; +} + static std::unique_ptr InferMetalCapabilities( id device, PixelFormat color_format) { @@ -71,6 +80,8 @@ static std::unique_ptr InferMetalCapabilities( .SetDefaultGlyphAtlasFormat(PixelFormat::kA8UNormInt) .SetSupportsTriangleFan(false) .SetMaximumRenderPassAttachmentSize(DeviceMaxTextureSizeSupported(device)) + .SetSupportsExtendedRangeFormats( + DeviceSupportsExtendedRangeFormats(device)) .Build(); } diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc b/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc index 5944feecf89..6e59d9f678c 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc @@ -798,4 +798,8 @@ bool CapabilitiesVK::SupportsExternalSemaphoreExtensions() const { return supports_external_fence_and_semaphore_; } +bool CapabilitiesVK::SupportsExtendedRangeFormats() const { + return false; +} + } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.h b/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.h index 38a984c8677..5b99f704289 100644 --- a/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.h +++ b/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.h @@ -260,6 +260,9 @@ class CapabilitiesVK final : public Capabilities, // |Capabilities| bool SupportsPrimitiveRestart() const override; + // |Capabilities| + bool SupportsExtendedRangeFormats() const override; + // |Capabilities| PixelFormat GetDefaultColorFormat() const override; diff --git a/engine/src/flutter/impeller/renderer/capabilities.cc b/engine/src/flutter/impeller/renderer/capabilities.cc index 5637d8458c3..3658086a3cf 100644 --- a/engine/src/flutter/impeller/renderer/capabilities.cc +++ b/engine/src/flutter/impeller/renderer/capabilities.cc @@ -91,6 +91,11 @@ class StandardCapabilities final : public Capabilities { // |Capabilities| bool SupportsPrimitiveRestart() const override { return true; } + // |Capabilities| + bool SupportsExtendedRangeFormats() const override { + return supports_extended_range_formats_; + } + private: StandardCapabilities(bool supports_offscreen_msaa, bool supports_ssbo, @@ -102,6 +107,7 @@ class StandardCapabilities final : public Capabilities { bool supports_decal_sampler_address_mode, bool supports_device_transient_textures, bool supports_triangle_fan, + bool supports_extended_range_formats, PixelFormat default_color_format, PixelFormat default_stencil_format, PixelFormat default_depth_stencil_format, @@ -118,6 +124,7 @@ class StandardCapabilities final : public Capabilities { supports_decal_sampler_address_mode), supports_device_transient_textures_(supports_device_transient_textures), supports_triangle_fan_(supports_triangle_fan), + supports_extended_range_formats_(supports_extended_range_formats), default_color_format_(default_color_format), default_stencil_format_(default_stencil_format), default_depth_stencil_format_(default_depth_stencil_format), @@ -137,6 +144,7 @@ class StandardCapabilities final : public Capabilities { bool supports_decal_sampler_address_mode_ = false; bool supports_device_transient_textures_ = false; bool supports_triangle_fan_ = false; + bool supports_extended_range_formats_ = false; PixelFormat default_color_format_ = PixelFormat::kUnknown; PixelFormat default_stencil_format_ = PixelFormat::kUnknown; PixelFormat default_depth_stencil_format_ = PixelFormat::kUnknown; @@ -238,6 +246,12 @@ CapabilitiesBuilder& CapabilitiesBuilder::SetMaximumRenderPassAttachmentSize( return *this; } +CapabilitiesBuilder& CapabilitiesBuilder::SetSupportsExtendedRangeFormats( + bool value) { + supports_extended_range_formats_ = value; + return *this; +} + std::unique_ptr CapabilitiesBuilder::Build() { return std::unique_ptr(new StandardCapabilities( // supports_offscreen_msaa_, // @@ -250,6 +264,7 @@ std::unique_ptr CapabilitiesBuilder::Build() { supports_decal_sampler_address_mode_, // supports_device_transient_textures_, // supports_triangle_fan_, // + supports_extended_range_formats_, // default_color_format_.value_or(PixelFormat::kUnknown), // default_stencil_format_.value_or(PixelFormat::kUnknown), // default_depth_stencil_format_.value_or(PixelFormat::kUnknown), // diff --git a/engine/src/flutter/impeller/renderer/capabilities.h b/engine/src/flutter/impeller/renderer/capabilities.h index 1a4d2015516..8e2ffc0b728 100644 --- a/engine/src/flutter/impeller/renderer/capabilities.h +++ b/engine/src/flutter/impeller/renderer/capabilities.h @@ -116,6 +116,13 @@ class Capabilities { /// Note that this may be smaller than the maximum allocatable texture size. virtual ISize GetMaximumRenderPassAttachmentSize() const = 0; + /// @brief Whether the XR formats are supported on this device. + /// + /// This is only ever true for iOS and macOS devices. We may need + /// to revisit this API when approaching wide gamut rendering for + /// Vulkan and GLES. + virtual bool SupportsExtendedRangeFormats() const = 0; + protected: Capabilities(); @@ -154,6 +161,8 @@ class CapabilitiesBuilder { CapabilitiesBuilder& SetSupportsDeviceTransientTextures(bool value); + CapabilitiesBuilder& SetSupportsExtendedRangeFormats(bool value); + CapabilitiesBuilder& SetDefaultGlyphAtlasFormat(PixelFormat value); CapabilitiesBuilder& SetSupportsTriangleFan(bool value); @@ -173,6 +182,7 @@ class CapabilitiesBuilder { bool supports_decal_sampler_address_mode_ = false; bool supports_device_transient_textures_ = false; bool supports_triangle_fan_ = false; + bool supports_extended_range_formats_ = false; std::optional default_color_format_ = std::nullopt; std::optional default_stencil_format_ = std::nullopt; std::optional default_depth_stencil_format_ = std::nullopt; diff --git a/engine/src/flutter/impeller/renderer/capabilities_unittests.cc b/engine/src/flutter/impeller/renderer/capabilities_unittests.cc index f4b83e9dd07..e0b59b227fd 100644 --- a/engine/src/flutter/impeller/renderer/capabilities_unittests.cc +++ b/engine/src/flutter/impeller/renderer/capabilities_unittests.cc @@ -28,6 +28,7 @@ CAPABILITY_TEST(SupportsReadFromResolve, false); CAPABILITY_TEST(SupportsDecalSamplerAddressMode, false); CAPABILITY_TEST(SupportsDeviceTransientTextures, false); CAPABILITY_TEST(SupportsTriangleFan, false); +CAPABILITY_TEST(SupportsExtendedRangeFormats, false); TEST(CapabilitiesTest, DefaultColorFormat) { auto defaults = CapabilitiesBuilder().Build(); diff --git a/engine/src/flutter/impeller/renderer/testing/mocks.h b/engine/src/flutter/impeller/renderer/testing/mocks.h index 6b4014c030f..1b1171a7318 100644 --- a/engine/src/flutter/impeller/renderer/testing/mocks.h +++ b/engine/src/flutter/impeller/renderer/testing/mocks.h @@ -223,6 +223,7 @@ class MockCapabilities : public Capabilities { MOCK_METHOD(bool, SupportsDeviceTransientTextures, (), (const, override)); MOCK_METHOD(bool, SupportsTriangleFan, (), (const override)); MOCK_METHOD(bool, SupportsPrimitiveRestart, (), (const override)); + MOCK_METHOD(bool, SupportsExtendedRangeFormats, (), (const override)); MOCK_METHOD(PixelFormat, GetDefaultColorFormat, (), (const, override)); MOCK_METHOD(PixelFormat, GetDefaultStencilFormat, (), (const, override)); MOCK_METHOD(PixelFormat, GetDefaultDepthStencilFormat, (), (const, override)); diff --git a/engine/src/flutter/lib/ui/painting/image_decoder_impeller.cc b/engine/src/flutter/lib/ui/painting/image_decoder_impeller.cc index 40b6d9e01c6..ba6a83e83bb 100644 --- a/engine/src/flutter/lib/ui/painting/image_decoder_impeller.cc +++ b/engine/src/flutter/lib/ui/painting/image_decoder_impeller.cc @@ -80,10 +80,10 @@ ImageDecoderImpeller::ImageDecoderImpeller( const TaskRunners& runners, std::shared_ptr concurrent_task_runner, const fml::WeakPtr& io_manager, - bool supports_wide_gamut, + bool wide_gamut_enabled, const std::shared_ptr& gpu_disabled_switch) : ImageDecoder(runners, std::move(concurrent_task_runner), io_manager), - supports_wide_gamut_(supports_wide_gamut), + wide_gamut_enabled_(wide_gamut_enabled), gpu_disabled_switch_(gpu_disabled_switch) { std::promise> context_promise; context_ = context_promise.get_future(); @@ -526,7 +526,7 @@ void ImageDecoderImpeller::Decode(fml::RefPtr descriptor, target_size = SkISize::Make(target_width, target_height), // io_runner = runners_.GetIOTaskRunner(), // result, - supports_wide_gamut = supports_wide_gamut_, // + wide_gamut_enabled = wide_gamut_enabled_, // gpu_disabled_switch = gpu_disabled_switch_]() { #if FML_OS_IOS_SIMULATOR // No-op backend. @@ -545,7 +545,8 @@ void ImageDecoderImpeller::Decode(fml::RefPtr descriptor, // Always decompress on the concurrent runner. auto bitmap_result = DecompressTexture( raw_descriptor, target_size, max_size_supported, - /*supports_wide_gamut=*/supports_wide_gamut, + /*supports_wide_gamut=*/wide_gamut_enabled && + context->GetCapabilities()->SupportsExtendedRangeFormats(), context->GetCapabilities(), context->GetResourceAllocator()); if (!bitmap_result.device_buffer) { result(nullptr, bitmap_result.decode_error); diff --git a/engine/src/flutter/lib/ui/painting/image_decoder_impeller.h b/engine/src/flutter/lib/ui/painting/image_decoder_impeller.h index 61952a21848..9376193a93e 100644 --- a/engine/src/flutter/lib/ui/painting/image_decoder_impeller.h +++ b/engine/src/flutter/lib/ui/painting/image_decoder_impeller.h @@ -101,7 +101,10 @@ class ImageDecoderImpeller final : public ImageDecoder { private: using FutureContext = std::shared_future>; FutureContext context_; - const bool supports_wide_gamut_; + + /// Whether wide gamut rendering has been enabled (but not necessarily whether + /// or not it is supported). + const bool wide_gamut_enabled_; std::shared_ptr gpu_disabled_switch_; /// Only call this method if the GPU is available.