[Impeller] add capability check for extended range formats. (#164817)

Fixes https://github.com/flutter/flutter/issues/164794

We support devices that do not support XR formats. If we try to decode
to an XR format this will fail at runtime.
This commit is contained in:
Jonah Williams 2025-03-07 18:43:08 -08:00 committed by GitHub
parent 6d6d7914f9
commit a7e276a20d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 61 additions and 5 deletions

View File

@ -219,6 +219,10 @@ bool CapabilitiesGLES::SupportsPrimitiveRestart() const {
return false; return false;
} }
bool CapabilitiesGLES::SupportsExtendedRangeFormats() const {
return false;
}
PixelFormat CapabilitiesGLES::GetDefaultGlyphAtlasFormat() const { PixelFormat CapabilitiesGLES::GetDefaultGlyphAtlasFormat() const {
return default_glyph_atlas_format_; return default_glyph_atlas_format_;
} }

View File

@ -116,6 +116,9 @@ class CapabilitiesGLES final
// |Capabilities| // |Capabilities|
bool SupportsPrimitiveRestart() const override; bool SupportsPrimitiveRestart() const override;
// |Capabilities|
bool SupportsExtendedRangeFormats() const override;
// |Capabilities| // |Capabilities|
PixelFormat GetDefaultColorFormat() const override; PixelFormat GetDefaultColorFormat() const override;

View File

@ -52,6 +52,15 @@ static bool DeviceSupportsComputeSubgroups(id<MTLDevice> device) {
return supports_subgroups; return supports_subgroups;
} }
// See "Extended Range and wide color pixel formats" in the metal feature set
// tables.
static bool DeviceSupportsExtendedRangeFormats(id<MTLDevice> device) {
if (@available(macOS 10.15, iOS 13, tvOS 13, *)) {
return [device supportsFamily:MTLGPUFamilyApple3];
}
return false;
}
static std::unique_ptr<Capabilities> InferMetalCapabilities( static std::unique_ptr<Capabilities> InferMetalCapabilities(
id<MTLDevice> device, id<MTLDevice> device,
PixelFormat color_format) { PixelFormat color_format) {
@ -71,6 +80,8 @@ static std::unique_ptr<Capabilities> InferMetalCapabilities(
.SetDefaultGlyphAtlasFormat(PixelFormat::kA8UNormInt) .SetDefaultGlyphAtlasFormat(PixelFormat::kA8UNormInt)
.SetSupportsTriangleFan(false) .SetSupportsTriangleFan(false)
.SetMaximumRenderPassAttachmentSize(DeviceMaxTextureSizeSupported(device)) .SetMaximumRenderPassAttachmentSize(DeviceMaxTextureSizeSupported(device))
.SetSupportsExtendedRangeFormats(
DeviceSupportsExtendedRangeFormats(device))
.Build(); .Build();
} }

View File

@ -798,4 +798,8 @@ bool CapabilitiesVK::SupportsExternalSemaphoreExtensions() const {
return supports_external_fence_and_semaphore_; return supports_external_fence_and_semaphore_;
} }
bool CapabilitiesVK::SupportsExtendedRangeFormats() const {
return false;
}
} // namespace impeller } // namespace impeller

View File

@ -260,6 +260,9 @@ class CapabilitiesVK final : public Capabilities,
// |Capabilities| // |Capabilities|
bool SupportsPrimitiveRestart() const override; bool SupportsPrimitiveRestart() const override;
// |Capabilities|
bool SupportsExtendedRangeFormats() const override;
// |Capabilities| // |Capabilities|
PixelFormat GetDefaultColorFormat() const override; PixelFormat GetDefaultColorFormat() const override;

View File

@ -91,6 +91,11 @@ class StandardCapabilities final : public Capabilities {
// |Capabilities| // |Capabilities|
bool SupportsPrimitiveRestart() const override { return true; } bool SupportsPrimitiveRestart() const override { return true; }
// |Capabilities|
bool SupportsExtendedRangeFormats() const override {
return supports_extended_range_formats_;
}
private: private:
StandardCapabilities(bool supports_offscreen_msaa, StandardCapabilities(bool supports_offscreen_msaa,
bool supports_ssbo, bool supports_ssbo,
@ -102,6 +107,7 @@ class StandardCapabilities final : public Capabilities {
bool supports_decal_sampler_address_mode, bool supports_decal_sampler_address_mode,
bool supports_device_transient_textures, bool supports_device_transient_textures,
bool supports_triangle_fan, bool supports_triangle_fan,
bool supports_extended_range_formats,
PixelFormat default_color_format, PixelFormat default_color_format,
PixelFormat default_stencil_format, PixelFormat default_stencil_format,
PixelFormat default_depth_stencil_format, PixelFormat default_depth_stencil_format,
@ -118,6 +124,7 @@ class StandardCapabilities final : public Capabilities {
supports_decal_sampler_address_mode), supports_decal_sampler_address_mode),
supports_device_transient_textures_(supports_device_transient_textures), supports_device_transient_textures_(supports_device_transient_textures),
supports_triangle_fan_(supports_triangle_fan), supports_triangle_fan_(supports_triangle_fan),
supports_extended_range_formats_(supports_extended_range_formats),
default_color_format_(default_color_format), default_color_format_(default_color_format),
default_stencil_format_(default_stencil_format), default_stencil_format_(default_stencil_format),
default_depth_stencil_format_(default_depth_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_decal_sampler_address_mode_ = false;
bool supports_device_transient_textures_ = false; bool supports_device_transient_textures_ = false;
bool supports_triangle_fan_ = false; bool supports_triangle_fan_ = false;
bool supports_extended_range_formats_ = false;
PixelFormat default_color_format_ = PixelFormat::kUnknown; PixelFormat default_color_format_ = PixelFormat::kUnknown;
PixelFormat default_stencil_format_ = PixelFormat::kUnknown; PixelFormat default_stencil_format_ = PixelFormat::kUnknown;
PixelFormat default_depth_stencil_format_ = PixelFormat::kUnknown; PixelFormat default_depth_stencil_format_ = PixelFormat::kUnknown;
@ -238,6 +246,12 @@ CapabilitiesBuilder& CapabilitiesBuilder::SetMaximumRenderPassAttachmentSize(
return *this; return *this;
} }
CapabilitiesBuilder& CapabilitiesBuilder::SetSupportsExtendedRangeFormats(
bool value) {
supports_extended_range_formats_ = value;
return *this;
}
std::unique_ptr<Capabilities> CapabilitiesBuilder::Build() { std::unique_ptr<Capabilities> CapabilitiesBuilder::Build() {
return std::unique_ptr<StandardCapabilities>(new StandardCapabilities( // return std::unique_ptr<StandardCapabilities>(new StandardCapabilities( //
supports_offscreen_msaa_, // supports_offscreen_msaa_, //
@ -250,6 +264,7 @@ std::unique_ptr<Capabilities> CapabilitiesBuilder::Build() {
supports_decal_sampler_address_mode_, // supports_decal_sampler_address_mode_, //
supports_device_transient_textures_, // supports_device_transient_textures_, //
supports_triangle_fan_, // supports_triangle_fan_, //
supports_extended_range_formats_, //
default_color_format_.value_or(PixelFormat::kUnknown), // default_color_format_.value_or(PixelFormat::kUnknown), //
default_stencil_format_.value_or(PixelFormat::kUnknown), // default_stencil_format_.value_or(PixelFormat::kUnknown), //
default_depth_stencil_format_.value_or(PixelFormat::kUnknown), // default_depth_stencil_format_.value_or(PixelFormat::kUnknown), //

View File

@ -116,6 +116,13 @@ class Capabilities {
/// Note that this may be smaller than the maximum allocatable texture size. /// Note that this may be smaller than the maximum allocatable texture size.
virtual ISize GetMaximumRenderPassAttachmentSize() const = 0; 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: protected:
Capabilities(); Capabilities();
@ -154,6 +161,8 @@ class CapabilitiesBuilder {
CapabilitiesBuilder& SetSupportsDeviceTransientTextures(bool value); CapabilitiesBuilder& SetSupportsDeviceTransientTextures(bool value);
CapabilitiesBuilder& SetSupportsExtendedRangeFormats(bool value);
CapabilitiesBuilder& SetDefaultGlyphAtlasFormat(PixelFormat value); CapabilitiesBuilder& SetDefaultGlyphAtlasFormat(PixelFormat value);
CapabilitiesBuilder& SetSupportsTriangleFan(bool value); CapabilitiesBuilder& SetSupportsTriangleFan(bool value);
@ -173,6 +182,7 @@ class CapabilitiesBuilder {
bool supports_decal_sampler_address_mode_ = false; bool supports_decal_sampler_address_mode_ = false;
bool supports_device_transient_textures_ = false; bool supports_device_transient_textures_ = false;
bool supports_triangle_fan_ = false; bool supports_triangle_fan_ = false;
bool supports_extended_range_formats_ = false;
std::optional<PixelFormat> default_color_format_ = std::nullopt; std::optional<PixelFormat> default_color_format_ = std::nullopt;
std::optional<PixelFormat> default_stencil_format_ = std::nullopt; std::optional<PixelFormat> default_stencil_format_ = std::nullopt;
std::optional<PixelFormat> default_depth_stencil_format_ = std::nullopt; std::optional<PixelFormat> default_depth_stencil_format_ = std::nullopt;

View File

@ -28,6 +28,7 @@ CAPABILITY_TEST(SupportsReadFromResolve, false);
CAPABILITY_TEST(SupportsDecalSamplerAddressMode, false); CAPABILITY_TEST(SupportsDecalSamplerAddressMode, false);
CAPABILITY_TEST(SupportsDeviceTransientTextures, false); CAPABILITY_TEST(SupportsDeviceTransientTextures, false);
CAPABILITY_TEST(SupportsTriangleFan, false); CAPABILITY_TEST(SupportsTriangleFan, false);
CAPABILITY_TEST(SupportsExtendedRangeFormats, false);
TEST(CapabilitiesTest, DefaultColorFormat) { TEST(CapabilitiesTest, DefaultColorFormat) {
auto defaults = CapabilitiesBuilder().Build(); auto defaults = CapabilitiesBuilder().Build();

View File

@ -223,6 +223,7 @@ class MockCapabilities : public Capabilities {
MOCK_METHOD(bool, SupportsDeviceTransientTextures, (), (const, override)); MOCK_METHOD(bool, SupportsDeviceTransientTextures, (), (const, override));
MOCK_METHOD(bool, SupportsTriangleFan, (), (const override)); MOCK_METHOD(bool, SupportsTriangleFan, (), (const override));
MOCK_METHOD(bool, SupportsPrimitiveRestart, (), (const override)); MOCK_METHOD(bool, SupportsPrimitiveRestart, (), (const override));
MOCK_METHOD(bool, SupportsExtendedRangeFormats, (), (const override));
MOCK_METHOD(PixelFormat, GetDefaultColorFormat, (), (const, override)); MOCK_METHOD(PixelFormat, GetDefaultColorFormat, (), (const, override));
MOCK_METHOD(PixelFormat, GetDefaultStencilFormat, (), (const, override)); MOCK_METHOD(PixelFormat, GetDefaultStencilFormat, (), (const, override));
MOCK_METHOD(PixelFormat, GetDefaultDepthStencilFormat, (), (const, override)); MOCK_METHOD(PixelFormat, GetDefaultDepthStencilFormat, (), (const, override));

View File

@ -80,10 +80,10 @@ ImageDecoderImpeller::ImageDecoderImpeller(
const TaskRunners& runners, const TaskRunners& runners,
std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner, std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
const fml::WeakPtr<IOManager>& io_manager, const fml::WeakPtr<IOManager>& io_manager,
bool supports_wide_gamut, bool wide_gamut_enabled,
const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch) const std::shared_ptr<fml::SyncSwitch>& gpu_disabled_switch)
: ImageDecoder(runners, std::move(concurrent_task_runner), io_manager), : 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) { gpu_disabled_switch_(gpu_disabled_switch) {
std::promise<std::shared_ptr<impeller::Context>> context_promise; std::promise<std::shared_ptr<impeller::Context>> context_promise;
context_ = context_promise.get_future(); context_ = context_promise.get_future();
@ -526,7 +526,7 @@ void ImageDecoderImpeller::Decode(fml::RefPtr<ImageDescriptor> descriptor,
target_size = SkISize::Make(target_width, target_height), // target_size = SkISize::Make(target_width, target_height), //
io_runner = runners_.GetIOTaskRunner(), // io_runner = runners_.GetIOTaskRunner(), //
result, result,
supports_wide_gamut = supports_wide_gamut_, // wide_gamut_enabled = wide_gamut_enabled_, //
gpu_disabled_switch = gpu_disabled_switch_]() { gpu_disabled_switch = gpu_disabled_switch_]() {
#if FML_OS_IOS_SIMULATOR #if FML_OS_IOS_SIMULATOR
// No-op backend. // No-op backend.
@ -545,7 +545,8 @@ void ImageDecoderImpeller::Decode(fml::RefPtr<ImageDescriptor> descriptor,
// Always decompress on the concurrent runner. // Always decompress on the concurrent runner.
auto bitmap_result = DecompressTexture( auto bitmap_result = DecompressTexture(
raw_descriptor, target_size, max_size_supported, 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()); context->GetCapabilities(), context->GetResourceAllocator());
if (!bitmap_result.device_buffer) { if (!bitmap_result.device_buffer) {
result(nullptr, bitmap_result.decode_error); result(nullptr, bitmap_result.decode_error);

View File

@ -101,7 +101,10 @@ class ImageDecoderImpeller final : public ImageDecoder {
private: private:
using FutureContext = std::shared_future<std::shared_ptr<impeller::Context>>; using FutureContext = std::shared_future<std::shared_ptr<impeller::Context>>;
FutureContext context_; 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<fml::SyncSwitch> gpu_disabled_switch_; std::shared_ptr<fml::SyncSwitch> gpu_disabled_switch_;
/// Only call this method if the GPU is available. /// Only call this method if the GPU is available.