blob: 6bdc04d8adf7d25e3efae814f245c6b8165a0d1f [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/android/compositor/compositor_view.h"
#include <android/bitmap.h>
#include <memory>
#include <vector>
#include "base/android/android_info.h"
#include "base/android/jni_android.h"
#include "base/command_line.h"
#include "base/containers/id_map.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/rand_util.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "base/trace_event/trace_event.h"
#include "cc/slim/layer.h"
#include "cc/slim/solid_color_layer.h"
#include "chrome/browser/android/compositor/layer/toolbar_layer.h"
#include "chrome/browser/android/compositor/layer_title_cache.h"
#include "chrome/browser/android/compositor/tab_content_manager.h"
#include "chrome/browser/ui/android/layouts/scene_layer.h"
#include "content/public/browser/android/compositor.h"
#include "content/public/browser/child_process_data.h"
#include "content/public/browser/peak_gpu_memory_tracker_factory.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/process_type.h"
#include "gpu/config/gpu_finch_features.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/android/resources/resource_manager.h"
#include "ui/android/resources/ui_resource_provider.h"
#include "ui/android/window_android.h"
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gl/gl_features.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/android/chrome_jni_headers/CompositorView_jni.h"
using base::android::JavaParamRef;
namespace android {
jboolean JNI_CompositorView_IsSurfaceControlEnabled(JNIEnv* env) {
return features::IsAndroidSurfaceControlEnabled();
}
jlong JNI_CompositorView_Init(
JNIEnv* env,
const JavaParamRef<jobject>& obj,
const JavaParamRef<jobject>& jwindow_android,
const JavaParamRef<jobject>& jtab_content_manager) {
CompositorView* view;
ui::WindowAndroid* window_android =
ui::WindowAndroid::FromJavaWindowAndroid(jwindow_android);
TabContentManager* tab_content_manager =
TabContentManager::FromJavaObject(jtab_content_manager);
DCHECK(tab_content_manager);
// TODO(clholgat): Remove the compositor tabstrip flag.
view = new CompositorView(env, obj, window_android, tab_content_manager);
if (tab_content_manager) {
tab_content_manager->SetUIResourceProvider(view->GetUIResourceProvider());
}
return reinterpret_cast<intptr_t>(view);
}
jboolean JNI_CompositorView_PreferRgb565ForDisplay(JNIEnv* env) {
return features::PreferRGB565ResourcesForDisplay();
}
CompositorView::CompositorView(JNIEnv* env,
const base::android::JavaRef<jobject>& obj,
ui::WindowAndroid* window_android,
TabContentManager* tab_content_manager)
: tab_content_manager_(tab_content_manager),
root_layer_(cc::slim::SolidColorLayer::Create()),
scene_layer_(nullptr),
current_surface_format_(0),
content_width_(0),
content_height_(0),
overlay_video_mode_(false),
overlay_immersive_ar_mode_(false),
overlay_xr_full_screen_mode_(false) {
content::BrowserChildProcessObserver::Add(this);
obj_.Reset(env, obj);
compositor_.reset(content::Compositor::Create(this, window_android));
root_layer_->SetIsDrawable(true);
root_layer_->SetBackgroundColor(SkColors::kWhite);
}
// Constructor for testing.
CompositorView::CompositorView(JNIEnv* env,
const base::android::JavaRef<jobject>& obj,
ui::WindowAndroid* window_android,
TabContentManager* tab_content_manager,
std::unique_ptr<content::Compositor> compositor)
: tab_content_manager_(tab_content_manager),
root_layer_(cc::slim::SolidColorLayer::Create()),
scene_layer_(nullptr),
current_surface_format_(0),
content_width_(0),
content_height_(0),
overlay_video_mode_(false),
overlay_immersive_ar_mode_(false),
overlay_xr_full_screen_mode_(false) {
content::BrowserChildProcessObserver::Add(this);
obj_.Reset(env, obj);
compositor_ = std::move(compositor);
root_layer_->SetIsDrawable(true);
root_layer_->SetBackgroundColor(SkColors::kWhite);
}
CompositorView::~CompositorView() {
content::BrowserChildProcessObserver::Remove(this);
tab_content_manager_->OnUIResourcesWereEvicted();
// Explicitly reset these scoped_ptrs here because otherwise we callbacks will
// try to access member variables during destruction.
compositor_.reset();
}
void CompositorView::Destroy(JNIEnv* env) {
delete this;
}
ui::ResourceManager* CompositorView::GetResourceManager() {
return compositor_ ? &compositor_->GetResourceManager() : nullptr;
}
base::android::ScopedJavaLocalRef<jobject> CompositorView::GetResourceManager(
JNIEnv* env) {
return compositor_->GetResourceManager().GetJavaObject();
}
void CompositorView::RecreateSurface() {
JNIEnv* env = base::android::AttachCurrentThread();
compositor_->SetSurface(nullptr, false, nullptr);
Java_CompositorView_recreateSurface(env, obj_);
}
void CompositorView::UpdateLayerTreeHost() {
std::optional<base::ElapsedTimer> timer;
if (base::ShouldRecordSubsampledMetric(0.01)) {
timer.emplace();
}
JNIEnv* env = base::android::AttachCurrentThread();
// TODO(wkorman): Rename JNI interface to onCompositorUpdateLayerTreeHost.
Java_CompositorView_onCompositorLayout(env, obj_);
if (timer) {
base::UmaHistogramCustomMicrosecondsTimes(
"Android.Compositor.UpdateLayerTree.Duration.Subsampled",
timer->Elapsed(), base::Microseconds(1), base::Milliseconds(30), 50);
}
}
void CompositorView::DidSwapFrame(int pending_frames) {
JNIEnv* env = base::android::AttachCurrentThread();
Java_CompositorView_didSwapFrame(env, obj_, pending_frames);
}
void CompositorView::DidSwapBuffers(const gfx::Size& swap_size) {
JNIEnv* env = base::android::AttachCurrentThread();
bool swapped_current_size =
swap_size == gfx::Size(content_width_, content_height_);
Java_CompositorView_didSwapBuffers(env, obj_, swapped_current_size);
}
base::WeakPtr<ui::UIResourceProvider> CompositorView::GetUIResourceProvider() {
return compositor_ ? compositor_->GetUIResourceProvider() : nullptr;
}
void CompositorView::SurfaceCreated(JNIEnv* env) {
compositor_->SetRootLayer(root_layer_);
current_surface_format_ = 0;
}
void CompositorView::SurfaceDestroyed(JNIEnv* env) {
compositor_->SetSurface(nullptr, false, nullptr);
current_surface_format_ = 0;
tab_content_manager_->OnUIResourcesWereEvicted();
}
std::optional<int> CompositorView::SurfaceChanged(
JNIEnv* env,
jint format,
jint width,
jint height,
bool can_be_used_with_surface_control,
const JavaParamRef<jobject>& surface,
const JavaParamRef<jobject>& browser_input_token) {
// Java View layout sometimes unexpectedly cause CompositorView to be sized so
// large that it exceeds the max texture size and memory on the device. This
// then subsequently causes the GPU process to crash loop. See
// crbug.com/369374760. Ignore these which is probably less bad than crashing
// the GPU process.
constexpr int kExcessiveSurfaceSize = 1000000;
if (width >= kExcessiveSurfaceSize || height >= kExcessiveSurfaceSize ||
width <= 0 || height <= 0) {
LOG(WARNING) << "Ignoring invalid surface size " << width << "x" << height;
return std::nullopt;
}
std::optional<int> surface_handle = std::nullopt;
DCHECK(surface);
if (current_surface_format_ != format) {
current_surface_format_ = format;
surface_handle = compositor_->SetSurface(
surface, can_be_used_with_surface_control, browser_input_token);
}
gfx::Size size = gfx::Size(width, height);
compositor_->SetWindowBounds(size);
content_width_ = size.width();
content_height_ = size.height();
root_layer_->SetBounds(gfx::Size(content_width_, content_height_));
return surface_handle;
}
void CompositorView::OnPhysicalBackingSizeChanged(
JNIEnv* env,
const JavaParamRef<jobject>& jweb_contents,
jint width,
jint height) {
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(jweb_contents);
gfx::Size size(width, height);
web_contents->GetNativeView()->OnPhysicalBackingSizeChanged(size);
}
void CompositorView::OnControlsResizeViewChanged(
JNIEnv* env,
const JavaParamRef<jobject>& jweb_contents,
jboolean controls_resize_view) {
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(jweb_contents);
web_contents->GetNativeView()->OnControlsResizeViewChanged(
controls_resize_view);
}
void CompositorView::NotifyVirtualKeyboardOverlayRect(
JNIEnv* env,
const JavaParamRef<jobject>& jweb_contents,
jint x,
jint y,
jint width,
jint height) {
content::WebContents* web_contents =
content::WebContents::FromJavaWebContents(jweb_contents);
gfx::Rect keyboard_rect(x, y, width, height);
web_contents->GetNativeView()->NotifyVirtualKeyboardOverlayRect(
keyboard_rect);
}
void CompositorView::SetLayoutBounds(JNIEnv* env) {
root_layer_->SetBounds(gfx::Size(content_width_, content_height_));
}
void CompositorView::SetBackground(bool visible, SkColor color) {
// TODO(crbug.com/41347744): Set the background color on the compositor.
// TODO(crbug.com/40219248): Remove FromColor and make all SkColor4f.
root_layer_->SetBackgroundColor(SkColor4f::FromColor(color));
root_layer_->SetIsDrawable(visible);
}
void CompositorView::SetOverlayVideoMode(JNIEnv* env,
bool enabled) {
if (overlay_video_mode_ == enabled) {
return;
}
overlay_video_mode_ = enabled;
compositor_->SetRequiresAlphaChannel(enabled);
SetNeedsComposite(env);
}
void CompositorView::SetOverlayImmersiveArMode(
JNIEnv* env,
bool enabled) {
DVLOG(1) << __func__ << ": enabled=" << enabled;
overlay_immersive_ar_mode_ = enabled;
// This method may be called after SetOverlayVideoMode (when switching between
// opaque and translucent surfaces), or just by itself (in SurfaceControl
// mode). All settings from SetOverlayVideoMode that the AR overlay depends on
// must be duplicated here. Currently, that's just SetRequiresAlphaChannel.
compositor_->SetRequiresAlphaChannel(enabled);
// This mode needs a transparent background color.
// ContentViewRenderView::SetOverlayVideoMode applies this, but the
// CompositorView::SetOverlayVideoMode version in this file doesn't.
compositor_->SetBackgroundColor(enabled ? SK_ColorTRANSPARENT
: SK_ColorWHITE);
compositor_->SetNeedsComposite();
}
void CompositorView::SetOverlayXrFullScreenMode(
JNIEnv* env,
bool enabled) {
if (overlay_xr_full_screen_mode_ == enabled) {
return;
}
overlay_xr_full_screen_mode_ = enabled;
// XR full screen mode requires a transparent background.
compositor_->SetBackgroundColor(enabled ? SK_ColorTRANSPARENT
: SK_ColorWHITE);
compositor_->SetNeedsComposite();
}
void CompositorView::SetSceneLayer(JNIEnv* env,
const JavaParamRef<jobject>& jscene_layer) {
SceneLayer* scene_layer = SceneLayer::FromJavaObject(env, jscene_layer);
if (scene_layer_ != scene_layer) {
// The old tree should be detached only if it is not the cached layer or
// the cached layer is not somewhere in the new root.
if (scene_layer_ &&
!scene_layer_->layer()->HasAncestor(scene_layer->layer().get())) {
scene_layer_->OnDetach();
}
scene_layer_ = scene_layer;
if (!scene_layer) {
return;
}
root_layer_->InsertChild(scene_layer->layer(), 0);
}
if (overlay_xr_full_screen_mode_ || overlay_immersive_ar_mode_) {
// Suppress the scene background's default background which breaks
// transparency. TODO(crbug.com/40098084): Remove this workaround
// once the issue with StaticTabSceneLayer's unexpected background is
// resolved.
bool should_show_background = scene_layer->ShouldShowBackground();
SkColor color = scene_layer->GetBackgroundColor();
if (should_show_background && color != SK_ColorTRANSPARENT) {
DVLOG(1) << "override non-transparent background 0x" << std::hex << color;
SetBackground(false, SK_ColorTRANSPARENT);
} else {
// No override needed, scene doesn't provide an opaque background.
SetBackground(should_show_background, color);
}
} else if (scene_layer) {
SetBackground(scene_layer->ShouldShowBackground(),
scene_layer->GetBackgroundColor());
} else {
#ifndef NDEBUG
// This should not happen. Setting red background just for debugging.
SetBackground(true, SK_ColorRED);
#else
SetBackground(true, SK_ColorBLACK);
#endif
}
}
void CompositorView::FinalizeLayers(JNIEnv* env) {
if (GetResourceManager()) {
GetResourceManager()->OnFrameUpdatesFinished();
}
#if !defined(OFFICIAL_BUILD)
TRACE_EVENT0("compositor", "CompositorView::FinalizeLayers");
#endif
}
void CompositorView::SetNeedsComposite(JNIEnv* env) {
compositor_->SetNeedsComposite();
}
void CompositorView::BrowserChildProcessKilled(
const content::ChildProcessData& data,
const content::ChildProcessTerminationInfo& info) {
LOG(WARNING) << "Child process died (type=" << data.process_type
<< ") pid=" << data.GetProcess().Pid() << ")";
// On Android R surface control layers leak if GPU process crashes, so we need
// to re-create surface to get rid of them.
if (data.process_type == content::PROCESS_TYPE_GPU) {
JNIEnv* env = base::android::AttachCurrentThread();
compositor_->SetSurface(nullptr, false, nullptr);
Java_CompositorView_recreateSurface(env, obj_);
}
}
void CompositorView::SetCompositorWindow(
JNIEnv* env,
const JavaParamRef<jobject>& window_android) {
ui::WindowAndroid* wa =
ui::WindowAndroid::FromJavaWindowAndroid(window_android);
compositor_->SetRootWindow(wa);
}
void CompositorView::CacheBackBufferForCurrentSurface(JNIEnv* env) {
compositor_->CacheBackBufferForCurrentSurface();
}
void CompositorView::EvictCachedBackBuffer(JNIEnv* env) {
compositor_->EvictCachedBackBuffer();
}
void CompositorView::OnTabChanged(JNIEnv* env) {
if (!compositor_) {
return;
}
std::unique_ptr<viz::PeakGpuMemoryTracker> tracker =
content::PeakGpuMemoryTrackerFactory::Create(
viz::PeakGpuMemoryTracker::Usage::CHANGE_TAB);
compositor_->RequestSuccessfulPresentationTimeForNextFrame(base::BindOnce(
[](std::unique_ptr<viz::PeakGpuMemoryTracker> tracker,
const viz::FrameTimingDetails& frame_timing_details) {
// This callback will be ran once the content::Compositor presents the
// next frame. The destruction of |tracker| will get the peak GPU memory
// and record a histogram.
},
std::move(tracker)));
}
void CompositorView::PreserveChildSurfaceControls(JNIEnv* env) {
compositor_->PreserveChildSurfaceControls();
}
void CompositorView::SetDidSwapBuffersCallbackEnabled(JNIEnv* env,
jboolean enable) {
compositor_->SetDidSwapBuffersCallbackEnabled(enable);
}
} // namespace android