Skip to content

[Impeller] Use a single convention to denote "empty" and "infinite/no limit" across all coverage/bounds/culling rectangles. #137306

@bdero

Description

@bdero

Currently we have at least two conventions, and we end up having to deal with both of them in EntityPass::GetSubpassCoverage, which is quite confusing:

  • For the EntityPass::GetSubpassCoverage coverage_limit parameter as well as CanvasStackEntry::cull_rect, std::nullopt means "infinite/no limit".
  • In the Contents::GetCoverage protocol, std::nullopt means "no/empty coverage", and Rect::MakeMaximum() is treated as "infinite/no limit should be applied".
  • In impeller::TRect, several methods (such as TRect::Intersection and TRect::Cutout) use std::nullopt to mean "no empty/no result".

We should pick a convention for coverage/bounds and stick with it everywhere.

Perhaps the least finicky and confusing standard would be to:

  • Always use an std::optional<Rect> for coverage/bounds/culling rectangles.
  • Treat Rect::IsEmpty() as meaning "no/empty coverage".
  • Treatstd::nullopt as "infinite/no limit".

Rationale:

  • Using Rect::MakeMaximum() to mean "infinite/no limit" isn't great, as it requires us to check for this case when doing rectangle arithmetic to avoid nonsense results. This has caused bugs surrounding infinite coverage in the past.
  • In some cases, infinite coverage is nonsense (for example, the coverage of a snapshot), and we'd be better off hard disallowing infinite coverage for these cases (for example, by storing coverage as a Rect instead of an std::optional<Rect>). Then, any codepaths that need to convert from "infinite-allowed" coverage (std::optional<Rect>) to "infinite-disallowed" coverage (Rect) need to explicitly handle or throw a validation error if infinite coverage is impossible.
  • Using std::nullopt to mean empty coverage is redundant. We allow rectangles to be zero size, and clearly Rect::IsEmpty is also "nothing/empty", but we currently ignore that case in most places, which is awkward.
  • One downside here with moving away from std::nullopt to mean "empty coverage" is that we'll lose a constant nag that forces us to think about empty coverage everywhere. However, the cases where empty coverage can be dangerous (like crashing because we can't allocate zero sized textures) have clear validations. The vast majority of the time, our empty coverage checks just serve to optimize by returning early rather than avoiding crashers.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work liste: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.team-engineOwned by Engine teamtriaged-engineTriaged by Engine team

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions