[Impeller] fix scaling of trampoline import of GLES textures into Vulkan. (#161331)

Not sure if this just needs the dpr transform or the entire canvas
transform.

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

## Before


![flutter_04](https://github.com/user-attachments/assets/be60b1d0-4e94-491c-b1da-a03d66897f12)


## After


![flutter_03](https://github.com/user-attachments/assets/64cca8b7-3ae7-4f40-af26-ae61d9ec6290)
This commit is contained in:
Jonah Williams 2025-01-09 12:39:29 -08:00 committed by GitHub
parent a38abc864c
commit 96dffbeda5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 211 additions and 5 deletions

View File

@ -13,6 +13,7 @@ import androidx.core.view.WindowInsetsControllerCompat
import com.example.android_engine_test.extensions.NativeDriverSupportPlugin
import com.example.android_engine_test.fixtures.BlueOrangeGradientPlatformViewFactory
import com.example.android_engine_test.fixtures.ChangingColorButtonPlatformViewFactory
import com.example.android_engine_test.fixtures.OtherFaceTexturePlugin
import com.example.android_engine_test.fixtures.SmileyFaceTexturePlugin
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
@ -25,6 +26,7 @@ class MainActivity : FlutterActivity() {
.plugins
.apply {
add(SmileyFaceTexturePlugin())
add(OtherFaceTexturePlugin())
add(NativeDriverSupportPlugin())
}

View File

@ -0,0 +1,91 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@file:Suppress("PackageName")
package com.example.android_engine_test.fixtures
import android.graphics.Color
import android.graphics.Paint
import android.os.Build
import android.view.Surface
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.view.TextureRegistry.SurfaceTextureEntry
class OtherFaceTexturePlugin :
FlutterPlugin,
MethodCallHandler {
private val tag = "OtherFaceTexturePlugin"
private lateinit var channel: MethodChannel
private lateinit var binding: FlutterPluginBinding
private lateinit var surfaceTextureEntry: SurfaceTextureEntry
private var surface: Surface? = null
override fun onAttachedToEngine(binding: FlutterPluginBinding) {
this.binding = binding
channel = MethodChannel(binding.binaryMessenger, "other_face_texture")
channel.setMethodCallHandler(this)
surfaceTextureEntry = binding.textureRegistry.createSurfaceTexture()
}
override fun onDetachedFromEngine(binding: FlutterPluginBinding) {
channel.setMethodCallHandler(null)
surfaceTextureEntry.release()
}
override fun onMethodCall(
call: MethodCall,
result: MethodChannel.Result
) {
if (call.method == "initTexture") {
val height = call.argument<Int>("height") ?: 1
val width = call.argument<Int>("width") ?: 1
surfaceTextureEntry.surfaceTexture().setDefaultBufferSize(width, height)
result.success(updateTexture())
} else {
result.notImplemented()
}
}
private fun updateTexture(): Long {
var surface = this.surface
if (surface == null) {
surface = Surface(surfaceTextureEntry.surfaceTexture())
this.surface = surface
}
drawOnSurface(surface!!)
return surfaceTextureEntry.id()
}
private fun drawOnSurface(surface: Surface) {
val canvas =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
surface.lockHardwareCanvas()
} else {
surface.lockCanvas(null)
}
// Yellow background
canvas.drawRGB(255, 230, 15)
val paint = Paint()
paint.style = Paint.Style.FILL
// Black eyes
paint.color = Color.BLACK
canvas.drawCircle(225f, 225f, 25f, paint) // Left eye
canvas.drawCircle(425f, 225f, 25f, paint) // Right eye
// Black mouth
paint.color = Color.BLACK
canvas.drawCircle(300f, 300f, 50f, paint) // Simple mouth
surface.unlockCanvasAndPost(canvas)
}
}

View File

@ -0,0 +1,54 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:android_driver_extensions/extension.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'src/allow_list_devices.dart';
const MethodChannel _channel = MethodChannel('other_face_texture');
Future<int> _fetchTexture(int width, int height) async {
final int? result = await _channel.invokeMethod<int>('initTexture', <String, int>{
'width': width,
'height': height,
});
return result!;
}
void main() async {
ensureAndroidOrIosDevice();
enableFlutterDriverExtension(commands: <CommandExtension>[nativeDriverCommands]);
// Run on full screen.
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
// Fetch the texture ID.
final Future<int> textureId = _fetchTexture(512, 512);
runApp(MainApp(textureId));
}
final class MainApp extends StatelessWidget {
const MainApp(this.textureId, {super.key});
final Future<int> textureId;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: FutureBuilder<int>(
future: textureId,
builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasData) {
return Texture(textureId: snapshot.data!);
}
return const CircularProgressIndicator();
},
),
);
}
}

View File

@ -0,0 +1,54 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:android_driver_extensions/native_driver.dart';
import 'package:android_driver_extensions/skia_gold.dart';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
import '_luci_skia_gold_prelude.dart';
void main() async {
// To test the golden file generation locally, comment out the following line.
// autoUpdateGoldenFiles = true;
const String appName = 'com.example.android_engine_test';
late final FlutterDriver flutterDriver;
late final NativeDriver nativeDriver;
setUpAll(() async {
if (isLuci) {
await enableSkiaGoldComparator();
}
flutterDriver = await FlutterDriver.connect();
nativeDriver = await AndroidNativeDriver.connect(flutterDriver);
await nativeDriver.configureForScreenshotTesting();
});
tearDownAll(() async {
await nativeDriver.close();
await flutterDriver.close();
});
test('should screenshot and match an external smiley face texture', () async {
await flutterDriver.waitFor(find.byType('Texture'));
// On Android: Background the app, trim memory, and restore the app.
if (nativeDriver case final AndroidNativeDriver nativeDriver) {
print('Backgrounding the app, trimming memory, and resuming the app.');
await nativeDriver.backgroundApp();
print('Trimming memory.');
await nativeDriver.simulateLowMemory(appName: appName);
print('Resuming the app.');
await nativeDriver.resumeApp(appName: appName);
}
await expectLater(
nativeDriver.screenshot(),
matchesGoldenFile('external_texture_other_face.android.png'),
);
}, timeout: Timeout.none);
}

View File

@ -122,15 +122,17 @@ void SurfaceTextureExternalTextureVKImpeller::ProcessFrame(
VALIDATION_LOG << "Invalid external texture.";
return;
}
SkMatrix matrix = context.canvas->GetTransform();
SkRect mapped_bounds = matrix.mapRect(bounds);
const auto& surface_context =
SurfaceContextVK::Cast(*context.aiks_context->GetContext());
const auto& context_vk = ContextVK::Cast(*surface_context.GetParent());
auto dst_texture =
GetCachedTextureSource(surface_context.GetParent(), //
ISize::MakeWH(bounds.width(), bounds.height()) //
);
auto dst_texture = GetCachedTextureSource(
surface_context.GetParent(), //
ISize::MakeWH(mapped_bounds.width(), mapped_bounds.height()) //
);
if (!dst_texture || !dst_texture->IsValid()) {
VALIDATION_LOG << "Could not fetch trampoline texture target.";
return;
@ -224,7 +226,10 @@ void SurfaceTextureExternalTextureVKImpeller::DrawFrame(
PaintContext& context,
const SkRect& bounds,
const DlImageSampling sampling) const {
context.canvas->DrawImage(dl_image_, SkPoint{0, 0}, sampling, context.paint);
context.canvas->DrawImageRect(
dl_image_,
SkRect::MakeSize(SkSize::Make(dl_image_->width(), dl_image_->height())),
bounds, sampling, context.paint);
}
} // namespace flutter