mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Sync presentation when rendering into FlutterImageView. (flutter/engine#44881)
Fixes https://github.com/flutter/flutter/issues/131730 When the Android embedder starts rendering into a FlutterImageView, notify the Impeller context to block on submitKHR.
This commit is contained in:
parent
a62803495b
commit
199e8abb3f
@ -83,6 +83,11 @@ class ContextVK final : public Context,
|
||||
// |Context|
|
||||
void Shutdown() override;
|
||||
|
||||
// |Context|
|
||||
void SetSyncPresentation(bool value) override { sync_presentation_ = value; }
|
||||
|
||||
bool GetSyncPresentation() const { return sync_presentation_; }
|
||||
|
||||
void SetOffscreenFormat(PixelFormat pixel_format);
|
||||
|
||||
template <typename T>
|
||||
@ -161,6 +166,7 @@ class ContextVK final : public Context,
|
||||
std::shared_ptr<ResourceManagerVK> resource_manager_;
|
||||
std::string device_name_;
|
||||
std::shared_ptr<fml::ConcurrentMessageLoop> raster_message_loop_;
|
||||
bool sync_presentation_ = false;
|
||||
const uint64_t hash_;
|
||||
|
||||
bool is_valid_ = false;
|
||||
|
||||
@ -81,6 +81,10 @@ std::unique_ptr<Surface> SurfaceContextVK::AcquireNextSurface() {
|
||||
return surface;
|
||||
}
|
||||
|
||||
void SurfaceContextVK::SetSyncPresentation(bool value) {
|
||||
parent_->SetSyncPresentation(value);
|
||||
}
|
||||
|
||||
#ifdef FML_OS_ANDROID
|
||||
|
||||
vk::UniqueSurfaceKHR SurfaceContextVK::CreateAndroidSurface(
|
||||
|
||||
@ -54,6 +54,9 @@ class SurfaceContextVK : public Context,
|
||||
// |Context|
|
||||
void Shutdown() override;
|
||||
|
||||
// |Context|
|
||||
void SetSyncPresentation(bool value) override;
|
||||
|
||||
[[nodiscard]] bool SetWindowSurface(vk::UniqueSurfaceKHR surface);
|
||||
|
||||
std::unique_ptr<Surface> AcquireNextSurface();
|
||||
|
||||
@ -465,48 +465,51 @@ bool SwapchainImplVK::Present(const std::shared_ptr<SwapchainImageVK>& image,
|
||||
}
|
||||
}
|
||||
|
||||
context.GetConcurrentWorkerTaskRunner()->PostTask(
|
||||
[&, index, image, current_frame = current_frame_] {
|
||||
auto context_strong = context_.lock();
|
||||
if (!context_strong) {
|
||||
return;
|
||||
}
|
||||
auto task = [&, index, image, current_frame = current_frame_] {
|
||||
auto context_strong = context_.lock();
|
||||
if (!context_strong) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& sync = synchronizers_[current_frame];
|
||||
const auto& sync = synchronizers_[current_frame];
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// Present the image.
|
||||
///
|
||||
uint32_t indices[] = {static_cast<uint32_t>(index)};
|
||||
//----------------------------------------------------------------------------
|
||||
/// Present the image.
|
||||
///
|
||||
uint32_t indices[] = {static_cast<uint32_t>(index)};
|
||||
|
||||
vk::PresentInfoKHR present_info;
|
||||
present_info.setSwapchains(*swapchain_);
|
||||
present_info.setImageIndices(indices);
|
||||
present_info.setWaitSemaphores(*sync->present_ready);
|
||||
vk::PresentInfoKHR present_info;
|
||||
present_info.setSwapchains(*swapchain_);
|
||||
present_info.setImageIndices(indices);
|
||||
present_info.setWaitSemaphores(*sync->present_ready);
|
||||
|
||||
switch (auto result = present_queue_.presentKHR(present_info)) {
|
||||
case vk::Result::eErrorOutOfDateKHR:
|
||||
// Caller will recreate the impl on acquisition, not submission.
|
||||
[[fallthrough]];
|
||||
case vk::Result::eErrorSurfaceLostKHR:
|
||||
// Vulkan guarantees that the set of queue operations will still
|
||||
// complete successfully.
|
||||
[[fallthrough]];
|
||||
case vk::Result::eSuboptimalKHR:
|
||||
// Even though we're handling rotation changes via polling, we
|
||||
// still need to handle the case where the swapchain signals that
|
||||
// it's suboptimal (i.e. every frame when we are rotated given we
|
||||
// aren't doing Vulkan pre-rotation).
|
||||
[[fallthrough]];
|
||||
case vk::Result::eSuccess:
|
||||
return;
|
||||
default:
|
||||
VALIDATION_LOG << "Could not present queue: "
|
||||
<< vk::to_string(result);
|
||||
return;
|
||||
}
|
||||
FML_UNREACHABLE();
|
||||
});
|
||||
switch (auto result = present_queue_.presentKHR(present_info)) {
|
||||
case vk::Result::eErrorOutOfDateKHR:
|
||||
// Caller will recreate the impl on acquisition, not submission.
|
||||
[[fallthrough]];
|
||||
case vk::Result::eErrorSurfaceLostKHR:
|
||||
// Vulkan guarantees that the set of queue operations will still
|
||||
// complete successfully.
|
||||
[[fallthrough]];
|
||||
case vk::Result::eSuboptimalKHR:
|
||||
// Even though we're handling rotation changes via polling, we
|
||||
// still need to handle the case where the swapchain signals that
|
||||
// it's suboptimal (i.e. every frame when we are rotated given we
|
||||
// aren't doing Vulkan pre-rotation).
|
||||
[[fallthrough]];
|
||||
case vk::Result::eSuccess:
|
||||
return;
|
||||
default:
|
||||
VALIDATION_LOG << "Could not present queue: " << vk::to_string(result);
|
||||
return;
|
||||
}
|
||||
FML_UNREACHABLE();
|
||||
};
|
||||
if (context.GetSyncPresentation()) {
|
||||
task();
|
||||
} else {
|
||||
context.GetConcurrentWorkerTaskRunner()->PostTask(task);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -161,6 +161,15 @@ class Context {
|
||||
///
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Force the Vulkan presentation (submitKHR) to be performed on
|
||||
/// the raster task runner.
|
||||
///
|
||||
/// This is required for correct rendering on Android when using
|
||||
/// the hybrid composition mode. This has no effect on other
|
||||
/// backends.
|
||||
virtual void SetSyncPresentation(bool value) {}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
/// @brief Accessor for a pool of HostBuffers.
|
||||
Pool<HostBuffer>& GetHostBufferPool() const { return host_buffer_pool_; }
|
||||
|
||||
@ -347,4 +347,8 @@ void AndroidShellHolder::UpdateDisplayMetrics() {
|
||||
shell_->OnDisplayUpdates(std::move(displays));
|
||||
}
|
||||
|
||||
void AndroidShellHolder::SetIsRenderingToImageView(bool value) {
|
||||
platform_view_->SetIsRenderingToImageView(value);
|
||||
}
|
||||
|
||||
} // namespace flutter
|
||||
|
||||
@ -105,6 +105,8 @@ class AndroidShellHolder {
|
||||
|
||||
void UpdateDisplayMetrics();
|
||||
|
||||
void SetIsRenderingToImageView(bool value);
|
||||
|
||||
private:
|
||||
const flutter::Settings settings_;
|
||||
const std::shared_ptr<PlatformViewAndroidJNI> jni_facade_;
|
||||
|
||||
@ -143,6 +143,7 @@ public class FlutterImageView extends View implements RenderSurface {
|
||||
switch (kind) {
|
||||
case background:
|
||||
flutterRenderer.swapSurface(imageReader.getSurface());
|
||||
flutterRenderer.SetRenderingToImageView(true);
|
||||
break;
|
||||
case overlay:
|
||||
// Do nothing since the attachment is done by the handler of
|
||||
@ -173,6 +174,12 @@ public class FlutterImageView extends View implements RenderSurface {
|
||||
closeCurrentImage();
|
||||
invalidate();
|
||||
isAttachedToFlutterRenderer = false;
|
||||
if (kind == SurfaceKind.background) {
|
||||
// The overlay FlutterImageViews seem to be constructed per frame and not
|
||||
// always used; An overlay FlutterImageView always seems to imply
|
||||
// a background FlutterImageView.
|
||||
flutterRenderer.SetRenderingToImageView(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
|
||||
@ -740,6 +740,14 @@ public class FlutterJNI {
|
||||
int[] displayFeaturesBounds,
|
||||
int[] displayFeaturesType,
|
||||
int[] displayFeaturesState);
|
||||
|
||||
@UiThread
|
||||
public void SetIsRenderingToImageView(boolean value) {
|
||||
nativeSetIsRenderingToImageView(nativeShellHolderId, value);
|
||||
}
|
||||
|
||||
private native void nativeSetIsRenderingToImageView(long nativeShellHolderId, boolean value);
|
||||
|
||||
// ----- End Render Surface Support -----
|
||||
|
||||
// ------ Start Touch Interaction Support ---
|
||||
|
||||
@ -50,6 +50,7 @@ public class FlutterRenderer implements TextureRegistry {
|
||||
@NonNull private final AtomicLong nextTextureId = new AtomicLong(0L);
|
||||
@Nullable private Surface surface;
|
||||
private boolean isDisplayingFlutterUi = false;
|
||||
private int isRenderingToImageViewCount = 0;
|
||||
private Handler handler = new Handler();
|
||||
|
||||
@NonNull
|
||||
@ -83,6 +84,19 @@ public class FlutterRenderer implements TextureRegistry {
|
||||
return isDisplayingFlutterUi;
|
||||
}
|
||||
|
||||
/**
|
||||
* Informs the renderer whether the surface it is rendering to is backend by a {@code
|
||||
* FlutterImageView}, which requires additional synchonization in the Vulkan backend.
|
||||
*/
|
||||
public void SetRenderingToImageView(boolean value) {
|
||||
if (value) {
|
||||
isRenderingToImageViewCount++;
|
||||
} else {
|
||||
isRenderingToImageViewCount--;
|
||||
}
|
||||
flutterJNI.SetIsRenderingToImageView(isRenderingToImageViewCount > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener that is invoked whenever this {@code FlutterRenderer} starts and stops painting
|
||||
* pixels to an Android {@code View} hierarchy.
|
||||
|
||||
@ -120,6 +120,12 @@ class PlatformViewAndroid final : public PlatformView {
|
||||
return platform_message_handler_;
|
||||
}
|
||||
|
||||
void SetIsRenderingToImageView(bool value) {
|
||||
if (GetImpellerContext()) {
|
||||
GetImpellerContext()->SetSyncPresentation(value);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::shared_ptr<PlatformViewAndroidJNI> jni_facade_;
|
||||
std::shared_ptr<AndroidContext> android_context_;
|
||||
|
||||
@ -550,6 +550,13 @@ static void NotifyLowMemoryWarning(JNIEnv* env,
|
||||
ANDROID_SHELL_HOLDER->NotifyLowMemoryWarning();
|
||||
}
|
||||
|
||||
static void SetIsRenderingToImageView(JNIEnv* env,
|
||||
jobject jcaller,
|
||||
jlong shell_holder,
|
||||
bool value) {
|
||||
ANDROID_SHELL_HOLDER->SetIsRenderingToImageView(value);
|
||||
}
|
||||
|
||||
static jboolean FlutterTextUtilsIsEmoji(JNIEnv* env,
|
||||
jobject obj,
|
||||
jint codePoint) {
|
||||
@ -709,6 +716,11 @@ bool RegisterApi(JNIEnv* env) {
|
||||
.signature = "(J)V",
|
||||
.fnPtr = reinterpret_cast<void*>(&NotifyLowMemoryWarning),
|
||||
},
|
||||
{
|
||||
.name = "nativeSetIsRenderingToImageView",
|
||||
.signature = "(JZ)V",
|
||||
.fnPtr = reinterpret_cast<void*>(&SetIsRenderingToImageView),
|
||||
},
|
||||
|
||||
// Start of methods from FlutterView
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user