[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;
}
bool CapabilitiesGLES::SupportsExtendedRangeFormats() const {
return false;
}
PixelFormat CapabilitiesGLES::GetDefaultGlyphAtlasFormat() const {
return default_glyph_atlas_format_;
}

View File

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

View File

@ -52,6 +52,15 @@ static bool DeviceSupportsComputeSubgroups(id<MTLDevice> device) {
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(
id<MTLDevice> device,
PixelFormat color_format) {
@ -71,6 +80,8 @@ static std::unique_ptr<Capabilities> InferMetalCapabilities(
.SetDefaultGlyphAtlasFormat(PixelFormat::kA8UNormInt)
.SetSupportsTriangleFan(false)
.SetMaximumRenderPassAttachmentSize(DeviceMaxTextureSizeSupported(device))
.SetSupportsExtendedRangeFormats(
DeviceSupportsExtendedRangeFormats(device))
.Build();
}

View File

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

View File

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

View File

@ -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<Capabilities> CapabilitiesBuilder::Build() {
return std::unique_ptr<StandardCapabilities>(new StandardCapabilities( //
supports_offscreen_msaa_, //
@ -250,6 +264,7 @@ std::unique_ptr<Capabilities> 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), //

View File

@ -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<PixelFormat> default_color_format_ = std::nullopt;
std::optional<PixelFormat> default_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(SupportsDeviceTransientTextures, false);
CAPABILITY_TEST(SupportsTriangleFan, false);
CAPABILITY_TEST(SupportsExtendedRangeFormats, false);
TEST(CapabilitiesTest, DefaultColorFormat) {
auto defaults = CapabilitiesBuilder().Build();

View File

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

View File

@ -80,10 +80,10 @@ ImageDecoderImpeller::ImageDecoderImpeller(
const TaskRunners& runners,
std::shared_ptr<fml::ConcurrentTaskRunner> concurrent_task_runner,
const fml::WeakPtr<IOManager>& io_manager,
bool supports_wide_gamut,
bool wide_gamut_enabled,
const std::shared_ptr<fml::SyncSwitch>& 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<std::shared_ptr<impeller::Context>> context_promise;
context_ = context_promise.get_future();
@ -526,7 +526,7 @@ void ImageDecoderImpeller::Decode(fml::RefPtr<ImageDescriptor> 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<ImageDescriptor> 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);

View File

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