Skip to content

[Impeller] matrix filter positioning needs to take partial repaint offset into account. #131305

@jonahwilliams

Description

@jonahwilliams

Run the following application on an iOS device with partial repaint enabled. Observe that the rectangle rotates offscreen instead of inplace. This is due to the following line:

  auto transform = is_subpass_ ? effect_transform.Basis()

Taking the basis discard the partial repaint translation. Of course, this was necessary to fix #130355 . Perhaps we need to track the partial repaint offset separately?

import 'package:flutter/material.dart';
import 'dart:math' as math;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  double _rotation = 0.0;

  static const _width = 100.0;
  static const _height = 100.0;

  static const _sliderBottom = 48.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: LayoutBuilder(
        builder: (context, constraints) {
          final x = (constraints.maxWidth - _width) / 2.0;
          final y = (constraints.maxHeight - _sliderBottom - _height) / 2.0;
          return Stack(
            fit: StackFit.expand,
            children: [
              Positioned.fromRect(
                rect: Rect.fromLTWH(x, y, _width, _height),
                child: Transform.rotate(
                  angle: _rotation,
                  filterQuality: FilterQuality.low,
                  child: Container(color: Colors.green),
                ),
              ),
              Positioned(
                left: 0.0,
                right: 0.0,
                bottom: _sliderBottom,
                child: RepaintBoundary(child: Slider(
                  value: _rotation,
                  min: 0.0,
                  max: math.pi * 1.5,
                  onChanged: (rotation) => setState(
                    () => _rotation = rotation,
                  ),
                )),
              )
            ],
          );
        },
      ),
    );
  }
}

In general the reason we do an offset and not a clip like the Skia backend is to avoid allocating a massive new texture, as we cannot use the existing framebuffer texture as an MSAA resolve without clearing it.

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work liste: impellerImpeller rendering backend issues and features requeststeam-engineOwned by Engine team

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions