Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi-view View Metrics #46174

Merged
merged 21 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/glfw/FlutterEmbedderGLFW.cc
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ void GLFWwindowSizeCallback(GLFWwindow* window, int width, int height) {
event.width = width * g_pixelRatio;
event.height = height * g_pixelRatio;
event.pixel_ratio = g_pixelRatio;
// This example only supports a single window, therefore we assume the event
// occurred in the only view, the implicit view.
event.view_id = kImplicitViewId;
FlutterEngineSendWindowMetricsEvent(
reinterpret_cast<FlutterEngine>(glfwGetWindowUserPointer(window)),
&event);
Expand Down
3 changes: 3 additions & 0 deletions examples/glfw_drm/FlutterEmbedderGLFW.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ void GLFWwindowSizeCallback(GLFWwindow* window, int width, int height) {
event.width = width * g_pixelRatio;
event.height = height * g_pixelRatio;
event.pixel_ratio = g_pixelRatio;
// This example only supports a single window, therefore we assume the event
// occurred in the only view, the implicit view.
event.view_id = kImplicitViewId;
FlutterEngineSendWindowMetricsEvent(
reinterpret_cast<FlutterEngine>(glfwGetWindowUserPointer(window)),
&event);
Expand Down
7 changes: 7 additions & 0 deletions examples/vulkan_glfw/src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ static const size_t kInitialWindowHeight = 600;
// `VK_PRESENT_MODE_MAILBOX_KHR` for continual swap without horizontal tearing,
// or `VK_PRESENT_MODE_IMMEDIATE_KHR` for no vsync.
static const VkPresentModeKHR kPreferredPresentMode = VK_PRESENT_MODE_FIFO_KHR;
static constexpr FlutterViewId kImplicitViewId = 0;

static_assert(FLUTTER_ENGINE_VERSION == 1,
"This Flutter Embedder was authored against the stable Flutter "
Expand Down Expand Up @@ -86,6 +87,9 @@ void GLFWcursorPositionCallbackAtPhase(GLFWwindow* window,
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
// This example only supports a single window, therefore we assume the event
// occurred in the only view, the implicit view.
event.view_id = kImplicitViewId;
FlutterEngineSendPointerEvent(g_state.engine, &event, 1);
}

Expand Down Expand Up @@ -130,6 +134,9 @@ void GLFWframebufferSizeCallback(GLFWwindow* window, int width, int height) {
event.width = width;
event.height = height;
event.pixel_ratio = g_pixelRatio;
// This example only supports a single window, therefore we assume the event
// occurred in the only view, the implicit view.
event.view_id = kImplicitViewId;
FlutterEngineSendWindowMetricsEvent(g_state.engine, &event);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -898,12 +898,6 @@ - (nonnull NSString*)executableName {
}

- (void)updateWindowMetricsForViewController:(FlutterViewController*)viewController {
if (viewController.viewId != kFlutterImplicitViewId) {
// TODO(dkwingsmt): The embedder API only supports single-view for now. As
// embedder APIs are converted to multi-view, this method should support any
// views.
return;
}
if (!_engine || !viewController || !viewController.viewLoaded) {
return;
}
Expand All @@ -922,6 +916,7 @@ - (void)updateWindowMetricsForViewController:(FlutterViewController*)viewControl
.left = static_cast<size_t>(scaledBounds.origin.x),
.top = static_cast<size_t>(scaledBounds.origin.y),
.display_id = static_cast<uint64_t>(displayId),
.view_id = viewController.viewId,
};
_embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
}
Expand Down
4 changes: 2 additions & 2 deletions shell/platform/embedder/embedder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2143,8 +2143,8 @@ FlutterEngineResult FlutterEngineSendWindowMetricsEvent(
if (engine == nullptr || flutter_metrics == nullptr) {
return LOG_EMBEDDER_ERROR(kInvalidArguments, "Engine handle was invalid.");
}
// TODO(dkwingsmt): Use a real view ID when multiview is supported.
int64_t view_id = kFlutterImplicitViewId;
FlutterViewId view_id =
SAFE_ACCESS(flutter_metrics, view_id, kFlutterImplicitViewId);

flutter::ViewportMetrics metrics;

Expand Down
2 changes: 2 additions & 0 deletions shell/platform/embedder/embedder.h
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,8 @@ typedef struct {
double physical_view_inset_left;
/// The identifier of the display the view is rendering on.
FlutterEngineDisplayId display_id;
/// The view that this event is describing.
int64_t view_id;
dkwingsmt marked this conversation as resolved.
Show resolved Hide resolved
} FlutterWindowMetricsEvent;

/// The phase of the pointer event.
Expand Down
36 changes: 36 additions & 0 deletions shell/platform/embedder/fixtures/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,42 @@ void pointer_data_packet_view_id() {
signalNativeTest();
}

Map<int, Size> _getAllViewSizes() {
final Map<int, Size> result = <int, Size>{};
for (final FlutterView view in PlatformDispatcher.instance.views) {
result[view.viewId] = view.physicalSize;
}
return result;
}

List<int> _findDifferences(Map<int, Size> a, Map<int, Size> b) {
final Set<int> result = <int>{};
a.forEach((int viewId, Size sizeA) {
if (!b.containsKey(viewId) || b[viewId] != sizeA) {
result.add(viewId);
}
});
b.forEach((int viewId, Size sizeB) {
if (!a.containsKey(viewId)) {
result.add(viewId);
}
});
return result.toList()..sort();
}

@pragma('vm:entry-point')
void window_metrics_event_view_id() {
Map<int, Size> sizes = _getAllViewSizes();
PlatformDispatcher.instance.onMetricsChanged = () {
final Map<int, Size> newSizes = _getAllViewSizes();
final List<int> differences = _findDifferences(sizes, newSizes);
sizes = newSizes;
signalNativeMessage('Changed: $differences');
};

signalNativeTest();
}

@pragma('vm:entry-point')
Future<void> channel_listener_response() async {
channelBuffers.setListener('test/listen',
Expand Down
99 changes: 98 additions & 1 deletion shell/platform/embedder/tests/embedder_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2726,7 +2726,7 @@ TEST_F(EmbedderTest, CanSendPointer) {

/// Send a pointer event to Dart and wait until the Dart code echos with the
/// view ID.
TEST_F(EmbedderTest, CanSendPointerWithViewId) {
TEST_F(EmbedderTest, CanSendPointerEventWithViewId) {
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
Expand Down Expand Up @@ -2766,6 +2766,103 @@ TEST_F(EmbedderTest, CanSendPointerWithViewId) {
message_latch.Wait();
}

TEST_F(EmbedderTest, WindowMetricsEventDefaultsToImplicitView) {
loic-sharma marked this conversation as resolved.
Show resolved Hide resolved
dkwingsmt marked this conversation as resolved.
Show resolved Hide resolved
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetDartEntrypoint("window_metrics_event_view_id");

fml::AutoResetWaitableEvent ready_latch, message_latch;
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
[&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));
context.AddNativeCallback(
"SignalNativeMessage",
CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
auto message = tonic::DartConverter<std::string>::FromDart(
Dart_GetNativeArgument(args, 0));
ASSERT_EQ("Changed: [0]", message);
message_latch.Signal();
}));

auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());

ready_latch.Wait();

FlutterWindowMetricsEvent event = {};
// Simulate an event that comes from an old version of embedder.h that doesn't
// have the view_id field.
event.struct_size = offsetof(FlutterWindowMetricsEvent, view_id);
event.width = 200;
event.height = 300;
event.pixel_ratio = 1.5;
// Skip assigning event.view_id here to test the default behavior.

FlutterEngineResult result =
FlutterEngineSendWindowMetricsEvent(engine.get(), &event);
ASSERT_EQ(result, kSuccess);

message_latch.Wait();
}

TEST_F(EmbedderTest, IgnoresWindowMetricsEventForUnknownView) {
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);
EmbedderConfigBuilder builder(context);
builder.SetSoftwareRendererConfig();
builder.SetDartEntrypoint("window_metrics_event_view_id");

fml::AutoResetWaitableEvent ready_latch, message_latch;
context.AddNativeCallback(
"SignalNativeTest",
CREATE_NATIVE_ENTRY(
[&ready_latch](Dart_NativeArguments args) { ready_latch.Signal(); }));

context.AddNativeCallback(
"SignalNativeMessage",
CREATE_NATIVE_ENTRY([&message_latch](Dart_NativeArguments args) {
auto message = tonic::DartConverter<std::string>::FromDart(
Dart_GetNativeArgument(args, 0));
// Message latch should only be signaled once as the bad
// view metric should be dropped by the engine.
ASSERT_FALSE(message_latch.IsSignaledForTest());
ASSERT_EQ("Changed: [0]", message);
message_latch.Signal();
}));

auto engine = builder.LaunchEngine();
ASSERT_TRUE(engine.is_valid());

ready_latch.Wait();

// Send a window metric for a nonexistent view, which should be dropped by the
// engine.
FlutterWindowMetricsEvent bad_event = {};
bad_event.struct_size = sizeof(FlutterWindowMetricsEvent);
bad_event.width = 200;
bad_event.height = 300;
bad_event.pixel_ratio = 1.5;
bad_event.view_id = 100;

FlutterEngineResult result =
FlutterEngineSendWindowMetricsEvent(engine.get(), &bad_event);
ASSERT_EQ(result, kSuccess);

// Send a window metric for a valid view. The engine notifies the Dart app.
FlutterWindowMetricsEvent event = {};
event.struct_size = sizeof(FlutterWindowMetricsEvent);
event.width = 200;
event.height = 300;
event.pixel_ratio = 1.5;
event.view_id = 0;

result = FlutterEngineSendWindowMetricsEvent(engine.get(), &event);
ASSERT_EQ(result, kSuccess);

message_latch.Wait();
}

TEST_F(EmbedderTest, RegisterChannelListener) {
auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext);

Expand Down
3 changes: 3 additions & 0 deletions shell/platform/glfw/flutter_glfw.cc
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,9 @@ static void SendWindowMetrics(FlutterDesktopWindowControllerState* controller,
} else {
event.pixel_ratio = controller->window_wrapper->pixel_ratio_override;
}
// The GLFW embedder doesn't support multiple views. We assume all pointer
// events come from the only view, the implicit view.
event.view_id = flutter::kFlutterImplicitViewId;
FlutterEngineSendWindowMetricsEvent(controller->engine->flutter_engine,
&event);
}
Expand Down
4 changes: 4 additions & 0 deletions shell/platform/linux/fl_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,10 @@ void fl_engine_send_window_metrics_event(FlEngine* self,
event.width = width;
event.height = height;
event.pixel_ratio = pixel_ratio;
// TODO(dkwingsmt): Assign the correct view ID once the Linux embedder
// supports multiple views.
// https://github.com/flutter/flutter/issues/138178
event.view_id = flutter::kFlutterImplicitViewId;
self->embedder_api.SendWindowMetricsEvent(self->engine, &event);
}

Expand Down
6 changes: 2 additions & 4 deletions shell/platform/windows/flutter_windows_view.cc
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ void FlutterWindowsView::SendWindowMetrics(size_t width,
event.width = width;
event.height = height;
event.pixel_ratio = dpiScale;
event.view_id = view_id_;
engine_->SendWindowMetricsEvent(event);
}

Expand Down Expand Up @@ -588,10 +589,7 @@ void FlutterWindowsView::SendPointerEventWithData(
event.device_kind = state->device_kind;
event.device = state->pointer_id;
event.buttons = state->buttons;
// TODO(dkwingsmt): Use the correct view ID for pointer events once the
// Windows embedder supports multiple views.
// https://github.com/flutter/flutter/issues/138179
event.view_id = flutter::kFlutterImplicitViewId;
event.view_id = view_id_;

// Set metadata that's always the same regardless of the event.
event.struct_size = sizeof(event);
Expand Down