mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[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  ## After 
This commit is contained in:
parent
a38abc864c
commit
96dffbeda5
@ -13,6 +13,7 @@ import androidx.core.view.WindowInsetsControllerCompat
|
|||||||
import com.example.android_engine_test.extensions.NativeDriverSupportPlugin
|
import com.example.android_engine_test.extensions.NativeDriverSupportPlugin
|
||||||
import com.example.android_engine_test.fixtures.BlueOrangeGradientPlatformViewFactory
|
import com.example.android_engine_test.fixtures.BlueOrangeGradientPlatformViewFactory
|
||||||
import com.example.android_engine_test.fixtures.ChangingColorButtonPlatformViewFactory
|
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 com.example.android_engine_test.fixtures.SmileyFaceTexturePlugin
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
@ -25,6 +26,7 @@ class MainActivity : FlutterActivity() {
|
|||||||
.plugins
|
.plugins
|
||||||
.apply {
|
.apply {
|
||||||
add(SmileyFaceTexturePlugin())
|
add(SmileyFaceTexturePlugin())
|
||||||
|
add(OtherFaceTexturePlugin())
|
||||||
add(NativeDriverSupportPlugin())
|
add(NativeDriverSupportPlugin())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
@ -122,14 +122,16 @@ void SurfaceTextureExternalTextureVKImpeller::ProcessFrame(
|
|||||||
VALIDATION_LOG << "Invalid external texture.";
|
VALIDATION_LOG << "Invalid external texture.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
SkMatrix matrix = context.canvas->GetTransform();
|
||||||
|
SkRect mapped_bounds = matrix.mapRect(bounds);
|
||||||
|
|
||||||
const auto& surface_context =
|
const auto& surface_context =
|
||||||
SurfaceContextVK::Cast(*context.aiks_context->GetContext());
|
SurfaceContextVK::Cast(*context.aiks_context->GetContext());
|
||||||
const auto& context_vk = ContextVK::Cast(*surface_context.GetParent());
|
const auto& context_vk = ContextVK::Cast(*surface_context.GetParent());
|
||||||
|
|
||||||
auto dst_texture =
|
auto dst_texture = GetCachedTextureSource(
|
||||||
GetCachedTextureSource(surface_context.GetParent(), //
|
surface_context.GetParent(), //
|
||||||
ISize::MakeWH(bounds.width(), bounds.height()) //
|
ISize::MakeWH(mapped_bounds.width(), mapped_bounds.height()) //
|
||||||
);
|
);
|
||||||
if (!dst_texture || !dst_texture->IsValid()) {
|
if (!dst_texture || !dst_texture->IsValid()) {
|
||||||
VALIDATION_LOG << "Could not fetch trampoline texture target.";
|
VALIDATION_LOG << "Could not fetch trampoline texture target.";
|
||||||
@ -224,7 +226,10 @@ void SurfaceTextureExternalTextureVKImpeller::DrawFrame(
|
|||||||
PaintContext& context,
|
PaintContext& context,
|
||||||
const SkRect& bounds,
|
const SkRect& bounds,
|
||||||
const DlImageSampling sampling) const {
|
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
|
} // namespace flutter
|
||||||
|
Loading…
Reference in New Issue
Block a user