Skip to content

[Impeller] Cache GLES sampler configuration in RenderPassStateCache - #193922

Merged
auto-submit[bot] merged 5 commits into
flutter:masterfrom
kevmoo:wimp-gles-sampler-cache-followup
Oct 14, 2026
Merged

auto-submit[bot] merged 5 commits into
flutter:masterfrom
kevmoo:wimp-gles-sampler-cache-followup

Conversation

@kevmoo

@kevmoo kevmoo commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Rationale

Split out from #193427 per @gaaclarke's review feedback.

OpenGL ES does not expose standalone sampler objects on the paths used by SamplerGLES; instead, SamplerGLES::ConfigureBoundTexture emits up to 5 glTexParameteri calls (GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MAX_LEVEL, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T) plus optional GL_TEXTURE_MAX_ANISOTROPY_EXT on the bound GL texture object every time a texture is bound for a draw call. On glyph-atlas-heavy and tiled scenes (bouncy@128, grid@1000), hundreds or thousands of consecutive draw calls sample the same texture with an identical SamplerDescriptor, issuing up to 12,258 redundant glTexParameteri/glTexParameterf calls per frame after #193427.

Within a render pass, non-wrapped TextureGLES instances own an immutable UniqueHandleGLES (HandleGLES) and immutable TextureDescriptor / mip_count. Caching the last configured SamplerDescriptor::ToKey per HandleGLES in SamplerStateCache inside RenderPassStateCache (alongside BlendStateCache, StencilStateCache, and DepthStateCache) skips redundant glTexParameter* calls across draws and pipelines within the pass without storing mutable cached state on TextureGLES.

Summary of Changes

Flow / Surface Delta

1. Deterministic WebGL2 Call-Count Reductions (0% Variance)

Measured on top of merged #193427 (origin/master @ a554b8ac180 vs. daad7727f54 across steady-state frames):

Workload (1280x720, wimp_st) Metric Pre-Change (post-#193427) Post-Change Absolute Delta Delta (%) Reduction
bouncy (128 nodes, 441 draws, 247 textured) glTexParameteri + glTexParameterf / frame 1,482 (1,235 + 247) ~6 (1 per pass) -1,476 -99.6% Deduplicated per pass
Total GL calls / frame 7,827 6,336 -1,491 -19.0% 1.24x fewer calls (-52.2% / 2.09x cumulative w/ #193427)
grid (100 nodes, 660 draws, 245 textured) glTexParameteri + glTexParameterf / frame 1,470 (1,225 + 245) ~6 (1 per pass) -1,464 -99.6% Deduplicated per pass
Total GL calls / frame 10,674 9,213 -1,461 -13.7% 1.16x fewer calls
grid (1000 nodes, 5,908 draws, 2,043 textured) glTexParameteri + glTexParameterf / frame 12,258 (10,215 + 2,043) ~6 (1 per pass) -12,252 -99.95% Deduplicated per pass
Total GL calls / frame 93,669 81,285 -12,384 -13.2% 1.15x fewer calls (-47.4% / 1.90x cumulative w/ #193427)

In 100us V8/Wasm CPU profiling of the DedicatedWorker raster thread on grid@1000 (--profile --no-strip-wasm), redundant glTexParameteri (9.75 ms Wasm self-time + 19.76 ms JS binding) and glTexParameterf (12.80 ms JS binding) accounted for 42.31 ms (2.09% of total active worker CPU time) across a 2.02s steady-state window.

2. Hardware WebGL2 rasterTimeMs A/B (AMD Radeon 780M, n=5 Interleaved ABBA Pairs)

Measured on headed Chrome 154 (ANGLE (AMD, AMD Radeon 780M Graphics (radeonsi phoenix ACO), OpenGL ES 3.2), --disable-gpu-vsync --disable-frame-rate-limit, 10 samples/run × 5 interleaved ABBA pairs per arm):

Workload (1280x720, wimp_st) Pre-Change (a554b8ac180) Post-Change (daad7727f54) Absolute Delta Delta (%) Speedup
bouncy (64 nodes) 1.412 ms 1.382 ms -0.030 ms -2.15% 1.02x
bouncy (128 nodes) 1.842 ms 1.756 ms -0.086 ms -4.66% 1.05x
grid (100 nodes) 1.783 ms 1.727 ms -0.057 ms -3.17% 1.03x
grid (1000 nodes) 16.385 ms 15.996 ms -0.389 ms -2.37% 1.02x

How much to trust these numbers:

  • Call counts (Table 1) are exact (0% run-to-run variance): Every frame deterministically eliminates 1,464–12,252 redundant glTexParameter* calls across the boundary.
  • Wall-clock rasterTimeMs (Table 2) has a ~1.2%–2.0% run-to-run noise floor:
    • bouncy@128 (-4.66%) and grid@100 (-3.17%) clear the noise floor consistently (5/5 interleaved Post runs beat Pre).
    • grid@1000 (-2.37%, -0.39 ms) sits just above the ~1.5%–2.0% noise floor (4/5 pairs faster) and aligns with the 2.09% worker CPU profile share of glTexParameter* when 5,908 draw calls dominate frame time.
    • bouncy@64 (-2.15%, -0.03 ms) is inside the noise floor at <1.5 ms frame times and should be treated as flat/within noise.

Verification

  • Built and ran host_debug_unopt:impeller_unittests --gtest_filter="*BufferBindingsGLES*:*RenderPassGLES*:*SamplerGLES*:*TextureGLES*" locally (37/37 passed, including BufferBindingsGLESTest.SkipsRedundantSamplerConfigurationOnSameTexture).
  • Built wasm_release:skwasm/wimp.js and verified both deterministic WebGL2 call counts and n=5 interleaved hardware WebGL2 rasterTimeMs medians (AMD Radeon 780M) in Chrome 154 via flutter-wasm-compare.

Related to #192147
Towards #174980

Pre-launch Checklist

OpenGL ES does not expose standalone sampler objects on the paths used by
SamplerGLES; instead, SamplerGLES::ConfigureBoundTexture emits up to 5
glTexParameteri calls (plus optional anisotropic filtering) on the bound
GL texture object every time a texture is bound for a draw call.

Cache the SamplerDescriptor::ToKey on non-wrapped TextureGLES instances so
consecutive draws sampling the same texture with an identical sampler
descriptor skip redundant glTexParameteri calls. External wrapped textures
(IsWrapped()) continue to apply sampler parameters unconditionally.

Fixes flutter#192147
@kevmoo kevmoo added the CICD Run CI/CD label Oct 6, 2026
@kevmoo
kevmoo requested a review from gaaclarke October 6, 2026 18:13

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an optimization in SamplerGLES to skip redundant sampler configuration calls by tracking the sampler state on non-wrapped textures using a new configured_sampler_key_ in TextureGLES. A corresponding unit test has been added to BufferBindingsGLESTest to verify this behavior. I have no further feedback to provide.

@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. e: impeller Impeller rendering backend issues and features requests labels Oct 6, 2026
kevmoo added a commit to kevmoo/flutter that referenced this pull request Oct 10, 2026
Move the per-texture sampler state cache out of `TextureGLES` and `SamplerGLES` into `SamplerStateCache` inside `RenderPassStateCache`, matching `BlendStateCache`, `StencilStateCache`, and `DepthStateCache`.

- Revert `sampler_gles.cc` to `master` and replace `configured_sampler_key_` on `TextureGLES` with a read-only `GetHandle()` accessor.
- Add `SamplerStateCache` in `buffer_bindings_gles.{h,cc}` keyed by `HandleGLES` (`HandleGLES::Hash` / `HandleGLES::Equal`).
- Own `SamplerStateCache sampler` inside `RenderPassStateCache` in `render_pass_gles.cc` and pass `&state_cache.sampler` into `BufferBindingsGLES::BindUniformData`.
- Expand `BufferBindingsGLESTest.SkipsRedundantSamplerConfigurationOnSameTexture` to verify cross-pipeline deduplication via a shared `SamplerStateCache`.
@kevmoo kevmoo changed the title [Impeller] Cache configured sampler descriptor key on TextureGLES [Impeller] Cache GLES sampler configuration in RenderPassStateCache Oct 10, 2026
@kevmoo
kevmoo requested a review from gaaclarke October 10, 2026 05:58

@gaaclarke gaaclarke left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, that's starting to look better.

… headers

- Convert `SamplerStateCache` from `struct` to `class` with private `configured_samplers_` and deleted copy operations per the Google C++ Style Guide.
- Replace forward declarations of `SamplerGLES` and `TextureGLES` in `buffer_bindings_gles.h` with direct `#include`s.
@kevmoo
kevmoo dismissed gaaclarke’s stale review October 13, 2026 17:58

Addressed in fac2aab (SamplerStateCache converted to class and forward declarations replaced with #includes); re-requesting review from @gaaclarke.

@kevmoo
kevmoo requested a review from gaaclarke October 13, 2026 17:58

@gaaclarke gaaclarke left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, thanks

@kevmoo kevmoo added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 14, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Oct 14, 2026
Merged via the queue into flutter:master with commit 6ed3be9 Oct 14, 2026
22 of 23 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 14, 2026
kevmoo added a commit to kevmoo/flutter that referenced this pull request Oct 14, 2026
- Merge origin/master (including flutter#193922 SamplerStateCache) into
  wimp-track-b-gles3-state-dedup.
- Convert struct VertexAttribStateCache to class VertexAttribStateCache with
  private data members and encapsulated state-tracking methods matching the
  review style from flutter#193922 and flutter#193427.
- DelegateSamplerStateCache::IsTextureConfigured in BindAndConfigureTexture.
@kevmoo
kevmoo deleted the wimp-gles-sampler-cache-followup branch October 14, 2026 04:50
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Oct 14, 2026
Roll Flutter from 4249ccbc18a3 to 426be968b5eb (44 revisions)

flutter/flutter@4249ccb...426be96

2026-10-14 [email protected] Allow color delta tolerance in in-memory fuzzy image comparator (flutter/flutter#193937)
2026-10-14 [email protected] [web] Drop stale composing range after text is replaced mid-composition (flutter/flutter#194096)
2026-10-14 [email protected] Roll Skia from 2a2bbea66844 to acde3755d2e2 (1 revision) (flutter/flutter#194376)
2026-10-14 [email protected] Roll Skia from 128c983b12de to 2a2bbea66844 (1 revision) (flutter/flutter#194371)
2026-10-14 [email protected] Roll Skia from 48a294ed4955 to 128c983b12de (3 revisions) (flutter/flutter#194353)
2026-10-14 [email protected] feat(flutter_tools): extract WebDebugConfig from DebuggingOptions (flutter/flutter#194312)
2026-10-14 [email protected] Roll Skia from ea6781c5b6d1 to 48a294ed4955 (2 revisions) (flutter/flutter#194349)
2026-10-14 [email protected] Include <vector> in Android proc_table.h. (flutter/flutter#194325)
2026-10-14 [email protected] Roll Dart SDK from 5933cb486b4a to 6b50116630d0 (28 revisions) (flutter/flutter#194341)
2026-10-14 [email protected] Move GLES3 instancing API entry points to FOR_EACH_IMPELLER_GLES3_PROC (flutter/flutter#194319)
2026-10-14 [email protected] feat(flutter_tools): extract AndroidGradleConfig and move toGradleConfig to AndroidBuildInfo (flutter/flutter#194016)
2026-10-14 [email protected] Roll Skia from ae297bec6012 to ea6781c5b6d1 (8 revisions) (flutter/flutter#194331)
2026-10-14 [email protected] [flutter_tools] Migrate gradle_errors and deferred_components_prebuild_validator away from globals (flutter/flutter#193649)
2026-10-14 [email protected] [Impeller] Cache GLES sampler configuration in RenderPassStateCache (flutter/flutter#193922)
2026-10-14 [email protected] Roll pub packages (flutter/flutter#194332)
2026-10-13 [email protected] [tool] Migrate RunCommand to modular dependency injection (flutter/flutter#190791)
2026-10-13 [email protected] Include `engine/src/flutter/.ci.yaml` in quarterly `goldctl` checklist (flutter/flutter#194316)
2026-10-13 [email protected] [flutter_tools] Remove the last AMD module system support (flutter/flutter#193607)
2026-10-13 [email protected] Roll Skia from 8b3394a60654 to ae297bec6012 (5 revisions) (flutter/flutter#194310)
2026-10-13 [email protected] [web] Remove the a11y placeholder only after the gesture ends (flutter/flutter#194109)
2026-10-13 [email protected] Upgrade goldctl to the latest version (flutter/flutter#193932)
2026-10-13 [email protected] [flutter_tools] Lazily initialize AndroidStudio validation and optimize candidate selection (flutter/flutter#191968)
2026-10-13 [email protected] [flutter_tools] Clean up outdated widget preview TODOs and refactor static state (flutter/flutter#189437)
2026-10-13 [email protected] Roll Skia from e5b5db6d0d6f to 8b3394a60654 (2 revisions) (flutter/flutter#194306)
2026-10-13 [email protected] Redistribute TESTOWNERS for Android team (flutter/flutter#193861)
2026-10-13 [email protected] feat(flutter_tools): introduce Test and Drive specification objects (flutter/flutter#194017)
2026-10-13 [email protected] [Impeller] Keep the aspect ratio when shrinking images to the max texture size (flutter/flutter#194010)
2026-10-13 [email protected] [flutter tool] preserve inherited `DASH__SUPPRESS_ANALYTICS=true` for sub-tools (flutter/flutter#194261)
2026-10-13 [email protected] Roll Packages from d8d9e97 to a1ed23c (9 revisions) (flutter/flutter#194302)
2026-10-13 [email protected] Use PE/COFF AOT snapshots for Windows x64 (flutter/flutter#187594)
2026-10-13 [email protected] Fix Expansible crash when PageStorage holds a non-bool value (flutter/flutter#192195)
2026-10-13 [email protected] Roll Skia from ffeccb3ca58a to e5b5db6d0d6f (1 revision) (flutter/flutter#194297)
2026-10-13 [email protected] Explain why initState() is too early to depend on an inherited widget (flutter/flutter#192727)
2026-10-13 [email protected] Refactor TestPlatformDispatcher (flutter/flutter#192747)
2026-10-13 [email protected] Roll Skia from 3de0984208ec to ffeccb3ca58a (2 revisions) (flutter/flutter#194291)
2026-10-13 [email protected] Roll Skia from 0580c2b780f6 to 3de0984208ec (2 revisions) (flutter/flutter#194281)
2026-10-13 [email protected] Roll Skia from f641d55e07a8 to 0580c2b780f6 (3 revisions) (flutter/flutter#194275)
2026-10-13 [email protected] Roll Skia from 0e5d8d1be81b to f641d55e07a8 (1 revision) (flutter/flutter#194266)
2026-10-13 [email protected] Scaffold view metrics overrides (flutter/flutter#192764)
2026-10-13 [email protected] [web] Add SemanticsRole.listBox and SemanticsRole.option (flutter/flutter#192971)
2026-10-13 [email protected] Pin mac_unopt to macOS 15 (flutter/flutter#194254)
2026-10-13 [email protected] Roll pub packages (flutter/flutter#194257)
2026-10-12 [email protected] Roll Skia from 17f6986c226c to 0e5d8d1be81b (4 revisions) (flutter/flutter#194249)
2026-10-12 [email protected] Make DisplayLists' use of DlPaint, SkPaint, and impeller::Paint agree on the default isAntiAlias (flutter/flutter#193939)

If this roll has caused a breakage, revert this CL and set the roller
...
suryaKommana2662 pushed a commit to suryaKommana2662/flutter that referenced this pull request Oct 29, 2026
…lutter#193922)

### Rationale

Split out from flutter#193427 per
@gaaclarke's review feedback.

OpenGL ES does not expose standalone sampler objects on the paths used
by `SamplerGLES`; instead, `SamplerGLES::ConfigureBoundTexture` emits up
to 5 `glTexParameteri` calls (`GL_TEXTURE_MIN_FILTER`,
`GL_TEXTURE_MAG_FILTER`, `GL_TEXTURE_MAX_LEVEL`, `GL_TEXTURE_WRAP_S`,
`GL_TEXTURE_WRAP_T`) plus optional `GL_TEXTURE_MAX_ANISOTROPY_EXT` on
the bound GL texture object every time a texture is bound for a draw
call. On glyph-atlas-heavy and tiled scenes (`bouncy@128`, `grid@1000`),
hundreds or thousands of consecutive draw calls sample the same texture
with an identical `SamplerDescriptor`, issuing up to `12,258` redundant
`glTexParameteri`/`glTexParameterf` calls per frame after
flutter#193427.

Within a render pass, non-wrapped `TextureGLES` instances own an
immutable `UniqueHandleGLES` (`HandleGLES`) and immutable
`TextureDescriptor` / `mip_count`. Caching the last configured
`SamplerDescriptor::ToKey` per `HandleGLES` in `SamplerStateCache`
inside `RenderPassStateCache` (alongside `BlendStateCache`,
`StencilStateCache`, and `DepthStateCache`) skips redundant
`glTexParameter*` calls across draws and pipelines within the pass
without storing mutable cached state on `TextureGLES`.

### Summary of Changes

-
[`engine/src/flutter/impeller/renderer/backend/gles/texture_gles.h`](https://github.com/kevmoo/flutter/blob/f9f4ba51255/engine/src/flutter/impeller/renderer/backend/gles/texture_gles.h):
Add a read-only `const HandleGLES& GetHandle() const` accessor matching
`PipelineGLES::GetProgramHandle()`.
-
[`engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.h`](https://github.com/kevmoo/flutter/blob/f9f4ba51255/engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.h)
&
[`engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc`](https://github.com/kevmoo/flutter/blob/f9f4ba51255/engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc):
Add `struct SamplerStateCache` (`absl::flat_hash_map<HandleGLES,
uint64_t, HandleGLES::Hash, HandleGLES::Equal> configured_samplers;` +
`ConfigureBoundTexture(...)`) and pass an optional `SamplerStateCache*`
into `BufferBindingsGLES::BindUniformData` and
`BufferBindingsGLES::BindTextures`. Wrapped external textures
(`IsWrapped()`) bypass the cache so external callers mutating raw GL
texture parameters are unaffected.
-
[`engine/src/flutter/impeller/renderer/backend/gles/render_pass_gles.cc`](https://github.com/kevmoo/flutter/blob/f9f4ba51255/engine/src/flutter/impeller/renderer/backend/gles/render_pass_gles.cc):
Add `SamplerStateCache sampler;` to `RenderPassStateCache` and pass
`&state_cache.sampler` into `vertex_desc_gles->BindUniformData(...)`.
-
[`engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles_unittests.cc`](https://github.com/kevmoo/flutter/blob/f9f4ba51255/engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles_unittests.cc):
Add
`BufferBindingsGLESTest.SkipsRedundantSamplerConfigurationOnSameTexture`
covering repeated binds with an identical sampler descriptor,
re-configuration when the sampler descriptor changes, cross-pipeline
deduplication/invalidation via a shared `SamplerStateCache`, and cache
bypass for wrapped external textures (`TextureGLES::WrapTexture`).

### Flow / Surface Delta

#### 1. Deterministic WebGL2 Call-Count Reductions (`0%` Variance)

Measured on top of merged flutter#193427
(`origin/master` @ `a554b8ac180` vs. `daad7727f54` across steady-state
frames):

| Workload (`1280x720`, `wimp_st`) | Metric | Pre-Change
(post-`flutter#193427`) | Post-Change | Absolute Delta | Delta (%) | Reduction
|
| :--- | :--- | :---: | :---: | :---: | :---: | :--- |
| **`bouncy` (`128` nodes, `441` draws, `247` textured)** |
`glTexParameteri` + `glTexParameterf` / frame | `1,482` (`1,235` +
`247`) | `~6` (`1` per pass) | `-1,476` | **`-99.6%`** | **Deduplicated
per pass** |
| | Total GL calls / frame | `7,827` | `6,336` | `-1,491` | **`-19.0%`**
| **`1.24x` fewer calls** (`-52.2%` / `2.09x` cumulative w/ `flutter#193427`) |
| **`grid` (`100` nodes, `660` draws, `245` textured)** |
`glTexParameteri` + `glTexParameterf` / frame | `1,470` (`1,225` +
`245`) | `~6` (`1` per pass) | `-1,464` | **`-99.6%`** | **Deduplicated
per pass** |
| | Total GL calls / frame | `10,674` | `9,213` | `-1,461` |
**`-13.7%`** | **`1.16x` fewer calls** |
| **`grid` (`1000` nodes, `5,908` draws, `2,043` textured)** |
`glTexParameteri` + `glTexParameterf` / frame | `12,258` (`10,215` +
`2,043`) | `~6` (`1` per pass) | `-12,252` | **`-99.95%`** |
**Deduplicated per pass** |
| | Total GL calls / frame | `93,669` | `81,285` | `-12,384` |
**`-13.2%`** | **`1.15x` fewer calls** (`-47.4%` / `1.90x` cumulative w/
`flutter#193427`) |

In `100us` V8/Wasm CPU profiling of the `DedicatedWorker` raster thread
on `grid@1000` (`--profile --no-strip-wasm`), redundant
`glTexParameteri` (`9.75 ms` Wasm self-time + `19.76 ms` JS binding) and
`glTexParameterf` (`12.80 ms` JS binding) accounted for **`42.31 ms`
(`2.09%` of total active worker CPU time)** across a `2.02s`
steady-state window.

#### 2. Hardware WebGL2 `rasterTimeMs` A/B (`AMD Radeon 780M`, `n=5`
Interleaved ABBA Pairs)

Measured on headed Chrome 154 (`ANGLE (AMD, AMD Radeon 780M Graphics
(radeonsi phoenix ACO), OpenGL ES 3.2)`, `--disable-gpu-vsync
--disable-frame-rate-limit`, `10` samples/run × `5` interleaved ABBA
pairs per arm):

| Workload (`1280x720`, `wimp_st`) | Pre-Change (`a554b8ac180`) |
Post-Change (`daad7727f54`) | Absolute Delta | Delta (%) | Speedup |
| :--- | :---: | :---: | :---: | :---: | :---: |
| **`bouncy` (`64` nodes)** | `1.412 ms` | `1.382 ms` | `-0.030 ms` |
`-2.15%` | `1.02x` |
| **`bouncy` (`128` nodes)** | `1.842 ms` | `1.756 ms` | `-0.086 ms` |
**`-4.66%`** | **`1.05x`** |
| **`grid` (`100` nodes)** | `1.783 ms` | `1.727 ms` | `-0.057 ms` |
**`-3.17%`** | **`1.03x`** |
| **`grid` (`1000` nodes)** | `16.385 ms` | `15.996 ms` | `-0.389 ms` |
**`-2.37%`** | **`1.02x`** |

**How much to trust these numbers:**
- **Call counts (Table 1) are exact (`0%` run-to-run variance)**: Every
frame deterministically eliminates `1,464`–`12,252` redundant
`glTexParameter*` calls across the boundary.
- **Wall-clock `rasterTimeMs` (Table 2) has a `~1.2%–2.0%` run-to-run
noise floor**:
- **`bouncy@128` (`-4.66%`)** and **`grid@100` (`-3.17%`)** clear the
noise floor consistently (`5/5` interleaved Post runs beat Pre).
- **`grid@1000` (`-2.37%`, `-0.39 ms`)** sits just above the
`~1.5%–2.0%` noise floor (`4/5` pairs faster) and aligns with the
`2.09%` worker CPU profile share of `glTexParameter*` when `5,908` draw
calls dominate frame time.
- **`bouncy@64` (`-2.15%`, `-0.03 ms`)** is inside the noise floor at
`<1.5 ms` frame times and should be treated as flat/within noise.

### Verification

- Built and ran `host_debug_unopt:impeller_unittests
--gtest_filter="*BufferBindingsGLES*:*RenderPassGLES*:*SamplerGLES*:*TextureGLES*"`
locally (`37/37` passed, including
`BufferBindingsGLESTest.SkipsRedundantSamplerConfigurationOnSameTexture`).
- Built `wasm_release:skwasm/wimp.js` and verified both deterministic
WebGL2 call counts and `n=5` interleaved hardware WebGL2 `rasterTimeMs`
medians (`AMD Radeon 780M`) in Chrome 154 via `flutter-wasm-compare`.

Related to flutter#192147
Towards flutter#174980

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [x] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD e: impeller Impeller rendering backend issues and features requests engine flutter/engine related. See also e: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants