Commit b1e1ac5d authored by Bo Liu's avatar Bo Liu Committed by Chromium LUCI CQ

Reland "aw: Switch instrumentation shell to SurfaceView"

This is a reland of 012b87aa

SetSurface is called when a surface is resized. On lollipop,
destroying and recreating an egl surface for the same java
surface fails, which then causes a CHECK failure. Instead,
if the same Surface instance is set again, just skip
native SetSurface.

Original change's description:
> aw: Switch instrumentation shell to SurfaceView
>
> Also refactor the fake draw_fn implementation. This is to prepare for
> adding a vulkan draw_fn implementation.
>
> Split draw_fn implementation into:
> allocator.cc/h: Controls functor data lifetime
> context_manager: Effectively global. Responsible for owning the surface
>                  and EGL context. Calls sync/draw/context_destroyed on
>                  functor and responsible for owning the "impl reference"
>                  to functor data.
>
> context_manager uses EGL bindings from ui/gl, but nothing else
> to avoid conflicting with chromium side implementations, and to avoid
> initializing a TaskRunner on render thread. Only GLES call is
> glReadPixels after draw, which relies on chromium implementation not
> releasing its bindings after draw.
>
> Refactor and simplify the java side by explicitly adding a thread to
> post tasks and a WaitableEvent; this avoids saving state and using
> a Lock object.
>
> Cq-Include-Trybots: luci.chromium.try:gpu-fyi-try-android-m-nexus-5x-64
> Bug: 1141687
> Change-Id: I71752b31f401fdb277062fe1347dfa826a20bb81
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2587238
> Auto-Submit: Bo <boliu@chromium.org>
> Commit-Queue: Jonathan Backer <backer@chromium.org>
> Reviewed-by: Jonathan Backer <backer@chromium.org>
> Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#837094}

Bug: 1141687
Change-Id: I2af0a20201aba8156bfa7e9fec6ac37729f5ab55
Cq-Include-Trybots: luci.chromium.try:gpu-fyi-try-android-m-nexus-5x-64
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2593878
Auto-Submit: Bo <boliu@chromium.org>
Commit-Queue: Jonathan Backer <backer@chromium.org>
Reviewed-by: default avatarVasiliy Telezhnikov <vasilyt@chromium.org>
Reviewed-by: default avatarJonathan Backer <backer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#837568}
parent 9d387cd5
......@@ -74,7 +74,7 @@ android_apk("webview_instrumentation_apk") {
"shell/src/org/chromium/android_webview/shell/AwShellApplication.java",
"shell/src/org/chromium/android_webview/shell/AwShellResourceProvider.java",
"shell/src/org/chromium/android_webview/shell/AwShellSwitches.java",
"shell/src/org/chromium/android_webview/shell/DrawFn.java",
"shell/src/org/chromium/android_webview/shell/ContextManager.java",
"shell/src/org/chromium/android_webview/test/AwJUnit4ClassRunner.java",
"shell/src/org/chromium/android_webview/test/AwTestContainerView.java",
"shell/src/org/chromium/android_webview/test/AwTestRunnerActivity.java",
......@@ -137,12 +137,24 @@ android_assets("webview_instrumentation_apk_assets") {
}
}
generate_jni("draw_fn_impl_jni_headers") {
sources =
[ "shell/src/org/chromium/android_webview/shell/ContextManager.java" ]
}
shared_library("libstandalonelibwebviewchromium") {
testonly = true
sources = [ "shell/src/draw_gl/draw_fn.cc" ]
sources = [
"shell/src/draw_fn/allocator.cc",
"shell/src/draw_fn/allocator.h",
"shell/src/draw_fn/context_manager.cc",
"shell/src/draw_fn/context_manager.h",
]
ldflags = [ "-Wl,-shared,-Bsymbolic" ]
deps = [
":draw_fn_impl_jni_headers",
":webview_instrumentation_test_native_jni_impl",
"//android_webview/browser/gfx",
"//android_webview/lib",
"//android_webview/lib:webview_entry_point",
"//android_webview/nonembedded",
......@@ -151,7 +163,9 @@ shared_library("libstandalonelibwebviewchromium") {
"//components/autofill/android/provider/test_support:component_autofill_provider_native_test_support",
"//components/heap_profiling/multi_process:test_support",
"//content/public/test/android:content_native_test_support",
"//ui/gl",
]
libs = [ "android" ]
configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
configs += [ "//build/config/android:hide_all_but_jni" ]
}
......
include_rules = [
"-android_webview",
"+android_webview/public/browser",
"+android_webview/test/draw_fn_impl_jni_headers",
"+android_webview/test/shell/src/draw_fn",
# For initializing GL bindings
"+android_webview/browser/gfx/gpu_service_web_view.h",
# For EGL bindings
"+ui/gl/gl_bindings.h",
]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "android_webview/test/shell/src/draw_fn/allocator.h"
#include "android_webview/public/browser/draw_fn.h"
#include "base/logging.h"
#include "base/notreached.h"
namespace draw_fn {
namespace {
AwDrawFnRenderMode QueryRenderMode() {
return AW_DRAW_FN_RENDER_MODE_OPENGL_ES;
}
int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
NOTREACHED();
return 0;
}
int CreateFunctor_v3(void* data,
int version,
AwDrawFnFunctorCallbacks* functor_callbacks) {
DCHECK_EQ(version, 3);
return Allocator::Get()->allocate(data, functor_callbacks);
}
void ReleaseFunctor(int functor) {
Allocator::Get()->MarkReleasedByFunctor(functor);
}
} // namespace
AwDrawFnFunctionTable* GetDrawFnFunctionTable() {
static AwDrawFnFunctionTable table{kAwDrawFnVersion, &QueryRenderMode,
&CreateFunctor, &ReleaseFunctor,
&CreateFunctor_v3};
return &table;
}
// static
Allocator* Allocator::Get() {
static base::NoDestructor<Allocator> map;
return map.get();
}
Allocator::Allocator() = default;
Allocator::~Allocator() = default;
int Allocator::allocate(void* data,
AwDrawFnFunctorCallbacks* functor_callbacks) {
base::AutoLock lock(lock_);
int functor = next_functor_++;
map_.emplace(functor, FunctorData{functor, data, functor_callbacks});
return functor;
}
FunctorData Allocator::get(int functor) {
base::AutoLock lock(lock_);
auto itr = map_.find(functor);
DCHECK(itr != map_.end());
return itr->second;
}
void Allocator::MarkReleasedByFunctor(int functor) {
base::AutoLock lock(lock_);
auto itr = map_.find(functor);
DCHECK(itr != map_.end());
DCHECK(!itr->second.released_by_functor);
itr->second.released_by_functor = true;
MaybeReleaseFunctorAlreadyLocked(functor);
}
void Allocator::MarkReleasedByManager(int functor) {
base::AutoLock lock(lock_);
auto itr = map_.find(functor);
DCHECK(itr != map_.end());
DCHECK(!itr->second.released_by_manager);
MaybeReleaseFunctorAlreadyLocked(functor);
}
void Allocator::MaybeReleaseFunctorAlreadyLocked(int functor) {
lock_.AssertAcquired();
auto itr = map_.find(functor);
const FunctorData& data = itr->second;
if (data.released_by_functor && data.released_by_manager) {
data.functor_callbacks->on_destroyed(data.functor, data.data);
map_.erase(itr);
}
}
} // namespace draw_fn
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ANDROID_WEBVIEW_TEST_SHELL_SRC_DRAW_FN_ALLOCATOR_H_
#define ANDROID_WEBVIEW_TEST_SHELL_SRC_DRAW_FN_ALLOCATOR_H_
#include "android_webview/public/browser/draw_fn.h"
#include "base/containers/flat_map.h"
#include "base/no_destructor.h"
#include "base/synchronization/lock.h"
namespace draw_fn {
AwDrawFnFunctionTable* GetDrawFnFunctionTable();
struct FunctorData {
int functor = 0;
void* data = nullptr;
AwDrawFnFunctorCallbacks* functor_callbacks = nullptr;
bool released_by_functor = false;
bool released_by_manager = false;
};
class Allocator {
public:
static Allocator* Get();
int allocate(void* data, AwDrawFnFunctorCallbacks* functor_callbacks);
FunctorData get(int functor);
void MarkReleasedByFunctor(int functor);
void MarkReleasedByManager(int functor);
private:
friend base::NoDestructor<Allocator>;
void MaybeReleaseFunctorAlreadyLocked(int functor);
Allocator();
~Allocator();
base::Lock lock_;
base::flat_map<int, FunctorData> map_;
int next_functor_ = 1;
};
} // namespace draw_fn
#endif // ANDROID_WEBVIEW_TEST_SHELL_SRC_DRAW_FN_ALLOCATOR_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "android_webview/test/shell/src/draw_fn/context_manager.h"
#include <android/native_window_jni.h>
#include "android_webview/browser/gfx/gpu_service_web_view.h"
#include "android_webview/public/browser/draw_fn.h"
#include "android_webview/test/draw_fn_impl_jni_headers/ContextManager_jni.h"
#include "android_webview/test/shell/src/draw_fn/allocator.h"
#include "base/android/jni_array.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "ui/gl/gl_bindings.h"
namespace draw_fn {
static jlong JNI_ContextManager_GetDrawFnFunctionTable(JNIEnv* env) {
return reinterpret_cast<intptr_t>(draw_fn::GetDrawFnFunctionTable());
}
static jlong JNI_ContextManager_Init(JNIEnv* env) {
return reinterpret_cast<intptr_t>(new ContextManager);
}
ContextManager::ContextManager() = default;
ContextManager::~ContextManager() {
DestroyContext();
}
void ContextManager::SetSurface(
JNIEnv* env,
const base::android::JavaRef<jobject>& surface) {
if (!java_surface_.is_null()) {
DestroyContext();
}
if (!surface.is_null()) {
CreateContext(env, surface);
}
}
void ContextManager::Sync(JNIEnv* env, int functor, bool apply_force_dark) {
if (current_functor_ && current_functor_ != functor) {
FunctorData data = Allocator::Get()->get(current_functor_);
data.functor_callbacks->on_context_destroyed(data.functor, data.data);
Allocator::Get()->MarkReleasedByManager(current_functor_);
}
current_functor_ = functor;
FunctorData data = Allocator::Get()->get(current_functor_);
AwDrawFn_OnSyncParams params{kAwDrawFnVersion, apply_force_dark};
data.functor_callbacks->on_sync(current_functor_, data.data, &params);
}
namespace {
ASurfaceControl* GetSurfaceControl() {
NOTREACHED();
return nullptr;
}
void MergeTransaction(ASurfaceTransaction* transaction) {
NOTREACHED();
}
EGLDisplay GetDisplay() {
static EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
CHECK_NE(display, EGL_NO_DISPLAY);
return display;
}
int rgbaToArgb(GLubyte* bytes) {
return (bytes[3] & 0xff) << 24 | (bytes[0] & 0xff) << 16 |
(bytes[1] & 0xff) << 8 | (bytes[2] & 0xff);
}
} // namespace
base::android::ScopedJavaLocalRef<jintArray> ContextManager::Draw(
JNIEnv* env,
int width,
int height,
int scroll_x,
int scroll_y,
jboolean readback_quadrants) {
int results[] = {0, 0, 0, 0};
if (!context_ || !current_functor_) {
LOG(ERROR) << "Draw failed. context:" << context_
<< " functor:" << current_functor_;
return readback_quadrants ? base::android::ToJavaIntArray(env, results)
: nullptr;
}
MakeCurrent();
FunctorData data = Allocator::Get()->get(current_functor_);
AwDrawFn_DrawGLParams params{kAwDrawFnVersion};
params.width = width;
params.height = height;
params.clip_left = 0;
params.clip_top = 0;
params.clip_bottom = height;
params.clip_right = width;
params.transform[0] = 1.0;
params.transform[1] = 0.0;
params.transform[2] = 0.0;
params.transform[3] = 0.0;
params.transform[4] = 0.0;
params.transform[5] = 1.0;
params.transform[6] = 0.0;
params.transform[7] = 0.0;
params.transform[8] = 0.0;
params.transform[9] = 0.0;
params.transform[10] = 1.0;
params.transform[11] = 0.0;
params.transform[12] = -scroll_x;
params.transform[13] = -scroll_y;
params.transform[14] = 0.0;
params.transform[15] = 1.0;
// Hard coded value for sRGB.
params.transfer_function_g = 2.4f;
params.transfer_function_a = 0.947867f;
params.transfer_function_b = 0.0521327f;
params.transfer_function_c = 0.0773994f;
params.transfer_function_d = 0.0404499f;
params.transfer_function_e = 0.f;
params.transfer_function_f = 0.f;
params.color_space_toXYZD50[0] = 0.436028f;
params.color_space_toXYZD50[1] = 0.385101f;
params.color_space_toXYZD50[2] = 0.143091f;
params.color_space_toXYZD50[3] = 0.222479f;
params.color_space_toXYZD50[4] = 0.716897f;
params.color_space_toXYZD50[5] = 0.0606241f;
params.color_space_toXYZD50[6] = 0.0139264f;
params.color_space_toXYZD50[7] = 0.0970921f;
params.color_space_toXYZD50[8] = 0.714191;
params.overlays_mode = AW_DRAW_FN_OVERLAYS_MODE_DISABLED;
params.get_surface_control = GetSurfaceControl;
params.merge_transaction = MergeTransaction;
data.functor_callbacks->draw_gl(current_functor_, data.data, &params);
if (readback_quadrants) {
int quarter_width = width / 4;
int quarter_height = height / 4;
GLubyte bytes[4] = {};
glReadPixels(quarter_width, quarter_height * 3, 1, 1, GL_RGBA,
GL_UNSIGNED_BYTE, bytes);
results[0] = rgbaToArgb(bytes);
glReadPixels(quarter_width * 3, quarter_height * 3, 1, 1, GL_RGBA,
GL_UNSIGNED_BYTE, bytes);
results[1] = rgbaToArgb(bytes);
glReadPixels(quarter_width, quarter_height, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
bytes);
results[2] = rgbaToArgb(bytes);
glReadPixels(quarter_width * 3, quarter_height, 1, 1, GL_RGBA,
GL_UNSIGNED_BYTE, bytes);
results[3] = rgbaToArgb(bytes);
}
CHECK(eglSwapBuffers(GetDisplay(), surface_));
return readback_quadrants ? base::android::ToJavaIntArray(env, results)
: nullptr;
}
namespace {
EGLConfig GetConfig(bool* out_use_es3) {
static EGLConfig config = nullptr;
static bool use_es3 = false;
if (config) {
*out_use_es3 = use_es3;
return config;
}
for (bool try_es3 : std::vector<bool>{true, false}) {
EGLint config_attribs[] = {
EGL_BUFFER_SIZE,
32,
EGL_ALPHA_SIZE,
8,
EGL_BLUE_SIZE,
8,
EGL_GREEN_SIZE,
8,
EGL_RED_SIZE,
8,
EGL_SAMPLES,
-1,
EGL_DEPTH_SIZE,
-1,
EGL_STENCIL_SIZE,
-1,
EGL_RENDERABLE_TYPE,
try_es3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
EGL_NONE};
EGLint num_configs = 0;
if (!eglChooseConfig(GetDisplay(), config_attribs, nullptr, 0,
&num_configs) ||
num_configs == 0) {
continue;
}
CHECK(eglChooseConfig(GetDisplay(), config_attribs, &config, 1,
&num_configs));
use_es3 = try_es3;
break;
}
CHECK(config);
*out_use_es3 = use_es3;
return config;
}
} // namespace
void ContextManager::CreateContext(
JNIEnv* env,
const base::android::JavaRef<jobject>& surface) {
// Initialize bindings.
android_webview::GpuServiceWebView::GetInstance();
java_surface_.Reset(surface);
if (java_surface_.is_null())
return;
native_window_ = ANativeWindow_fromSurface(env, surface.obj());
CHECK(native_window_);
bool use_es3 = false;
{
std::vector<EGLint> egl_window_attributes;
egl_window_attributes.push_back(EGL_NONE);
surface_ =
eglCreateWindowSurface(GetDisplay(), GetConfig(&use_es3),
native_window_, &egl_window_attributes[0]);
CHECK(surface_);
}
{
std::vector<EGLint> context_attributes;
context_attributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
context_attributes.push_back(use_es3 ? 3 : 2);
context_attributes.push_back(EGL_NONE);
CHECK(eglBindAPI(EGL_OPENGL_ES_API));
context_ = eglCreateContext(GetDisplay(), GetConfig(&use_es3), nullptr,
context_attributes.data());
CHECK(context_);
}
}
void ContextManager::DestroyContext() {
if (java_surface_.is_null())
return;
if (current_functor_) {
MakeCurrent();
FunctorData data = Allocator::Get()->get(current_functor_);
data.functor_callbacks->on_context_destroyed(data.functor, data.data);
}
DCHECK(context_);
CHECK(eglDestroyContext(GetDisplay(), context_));
context_ = nullptr;
DCHECK(surface_);
CHECK(eglDestroySurface(GetDisplay(), surface_));
surface_ = nullptr;
ANativeWindow_release(native_window_);
java_surface_.Reset();
}
void ContextManager::MakeCurrent() {
DCHECK(surface_);
DCHECK(context_);
CHECK(eglMakeCurrent(GetDisplay(), surface_, surface_, context_));
}
} // namespace draw_fn
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ANDROID_WEBVIEW_TEST_SHELL_SRC_DRAW_FN_CONTEXT_MANAGER_H_
#define ANDROID_WEBVIEW_TEST_SHELL_SRC_DRAW_FN_CONTEXT_MANAGER_H_
#include <android/native_window.h>
#include <jni.h>
#include "base/android/scoped_java_ref.h"
typedef void* EGLContext;
typedef void* EGLSurface;
namespace draw_fn {
class ContextManager {
public:
ContextManager();
~ContextManager();
void SetSurface(JNIEnv* env, const base::android::JavaRef<jobject>& surface);
void Sync(JNIEnv* env, int functor, bool apply_force_dark);
base::android::ScopedJavaLocalRef<jintArray> Draw(
JNIEnv* env,
int width,
int height,
int scroll_x,
int scroll_y,
jboolean readback_quadrants);
private:
void CreateContext(JNIEnv* env,
const base::android::JavaRef<jobject>& surface);
void DestroyContext();
void MakeCurrent();
base::android::ScopedJavaGlobalRef<jobject> java_surface_;
ANativeWindow* native_window_ = nullptr;
EGLSurface surface_ = nullptr;
EGLContext context_ = nullptr;
int current_functor_ = 0;
};
} // namespace draw_fn
#endif // ANDROID_WEBVIEW_TEST_SHELL_SRC_DRAW_FN_CONTEXT_MANAGER_H_
include_rules = [
# draw_gl compiles its own shared library for a single entry point
# for testing. Therefore it cannot depend on any other module,
# except for shared definitions.
"-android_webview",
"+android_webview/public/browser",
]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <jni.h>
#include "android_webview/public/browser/draw_fn.h"
#include "base/containers/flat_map.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/synchronization/lock.h"
namespace {
struct FunctorData {
int functor = 0;
void* data = nullptr;
AwDrawFnFunctorCallbacks* functor_callbacks = nullptr;
bool released = false;
};
class FunctorMap {
public:
int allocate(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
base::AutoLock lock(lock_);
int functor = next_functor_++;
map_.emplace(functor, FunctorData{functor, data, functor_callbacks});
return functor;
}
FunctorData get(int functor) {
base::AutoLock lock(lock_);
auto itr = map_.find(functor);
DCHECK(itr != map_.end());
return itr->second;
}
void mark_released(int functor) {
base::AutoLock lock(lock_);
auto itr = map_.find(functor);
DCHECK(itr != map_.end());
DCHECK(!itr->second.released);
itr->second.released = true;
}
void destroy_released() {
base::AutoLock lock(lock_);
for (auto itr = map_.begin(); itr != map_.end();) {
FunctorData& data = itr->second;
if (data.released) {
// Holding lock here, but not too terrible.
data.functor_callbacks->on_context_destroyed(data.functor, data.data);
data.functor_callbacks->on_destroyed(data.functor, data.data);
itr = map_.erase(itr);
} else {
DLOG(ERROR) << "Functor not released. Possibly leaking instead";
++itr;
}
}
}
static FunctorMap* Get() {
static base::NoDestructor<FunctorMap> map;
return map.get();
}
private:
base::Lock lock_;
base::flat_map<int, FunctorData> map_;
int next_functor_ = 1;
};
AwDrawFnRenderMode QueryRenderMode() {
return AW_DRAW_FN_RENDER_MODE_OPENGL_ES;
}
int CreateFunctor(void* data, AwDrawFnFunctorCallbacks* functor_callbacks) {
DCHECK(false);
NOTREACHED();
return 0;
}
int CreateFunctor_v3(void* data,
int version,
AwDrawFnFunctorCallbacks* functor_callbacks) {
DCHECK_EQ(version, 3);
return FunctorMap::Get()->allocate(data, functor_callbacks);
}
void ReleaseFunctor(int functor) {
FunctorMap::Get()->mark_released(functor);
}
ASurfaceControl* GetSurfaceControl() {
NOTREACHED();
return nullptr;
}
void MergeTransaction(ASurfaceTransaction* transaction) {
NOTREACHED();
}
} // namespace
extern "C" {
JNIEXPORT jlong JNICALL
Java_org_chromium_android_1webview_shell_DrawFn_nativeGetDrawFnFunctionTable(
JNIEnv*,
jclass) {
static AwDrawFnFunctionTable table{kAwDrawFnVersion, &QueryRenderMode,
&CreateFunctor, &ReleaseFunctor,
&CreateFunctor_v3};
return reinterpret_cast<intptr_t>(&table);
}
JNIEXPORT void JNICALL
Java_org_chromium_android_1webview_shell_DrawFn_nativeSync(
JNIEnv*,
jclass,
jint functor,
jboolean force_apply_dark) {
FunctorData data = FunctorMap::Get()->get(functor);
AwDrawFn_OnSyncParams params{kAwDrawFnVersion, force_apply_dark};
data.functor_callbacks->on_sync(functor, data.data, &params);
}
JNIEXPORT void JNICALL
Java_org_chromium_android_1webview_shell_DrawFn_nativeDestroyReleased(JNIEnv*,
jclass) {
FunctorMap::Get()->destroy_released();
}
JNIEXPORT void JNICALL
Java_org_chromium_android_1webview_shell_DrawFn_nativeDestroyed(JNIEnv*,
jclass,
jint functor) {}
JNIEXPORT void JNICALL
Java_org_chromium_android_1webview_shell_DrawFn_nativeDrawGL(JNIEnv*,
jclass,
jint functor,
jint width,
jint height,
jint scroll_x,
jint scroll_y) {
FunctorData data = FunctorMap::Get()->get(functor);
AwDrawFn_DrawGLParams params{kAwDrawFnVersion};
params.width = width;
params.height = height;
params.clip_left = 0;
params.clip_top = 0;
params.clip_bottom = height;
params.clip_right = width;
params.transform[0] = 1.0;
params.transform[1] = 0.0;
params.transform[2] = 0.0;
params.transform[3] = 0.0;
params.transform[4] = 0.0;
params.transform[5] = 1.0;
params.transform[6] = 0.0;
params.transform[7] = 0.0;
params.transform[8] = 0.0;
params.transform[9] = 0.0;
params.transform[10] = 1.0;
params.transform[11] = 0.0;
params.transform[12] = -scroll_x;
params.transform[13] = -scroll_y;
params.transform[14] = 0.0;
params.transform[15] = 1.0;
// Hard coded value for sRGB.
params.transfer_function_g = 2.4f;
params.transfer_function_a = 0.947867f;
params.transfer_function_b = 0.0521327f;
params.transfer_function_c = 0.0773994f;
params.transfer_function_d = 0.0404499f;
params.transfer_function_e = 0.f;
params.transfer_function_f = 0.f;
params.color_space_toXYZD50[0] = 0.436028f;
params.color_space_toXYZD50[1] = 0.385101f;
params.color_space_toXYZD50[2] = 0.143091f;
params.color_space_toXYZD50[3] = 0.222479f;
params.color_space_toXYZD50[4] = 0.716897f;
params.color_space_toXYZD50[5] = 0.0606241f;
params.color_space_toXYZD50[6] = 0.0139264f;
params.color_space_toXYZD50[7] = 0.0970921f;
params.color_space_toXYZD50[8] = 0.714191;
params.overlays_mode = AW_DRAW_FN_OVERLAYS_MODE_DISABLED;
params.get_surface_control = GetSurfaceControl;
params.merge_transaction = MergeTransaction;
data.functor_callbacks->draw_gl(functor, data.data, &params);
}
} // extern "C"
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview.shell;
import android.view.Surface;
import org.chromium.base.annotations.JNINamespace;
/** draw_fn framework side implementation for tests. */
@JNINamespace("draw_fn")
public class ContextManager {
private Surface mCurrentSurface;
public static long getDrawFnFunctionTable() {
return nativeGetDrawFnFunctionTable();
}
private final long mNativeContextManager;
public ContextManager() {
mNativeContextManager = nativeInit();
}
public void setSurface(Surface surface) {
if (mCurrentSurface == surface) return;
mCurrentSurface = surface;
nativeSetSurface(mNativeContextManager, surface);
}
public void sync(int functor, boolean applyForceDark) {
nativeSync(mNativeContextManager, functor, applyForceDark);
}
// Uses functor from last sync.
public int[] draw(int width, int height, int scrollX, int scrollY, boolean readbackQuadrants) {
return nativeDraw(
mNativeContextManager, width, height, scrollX, scrollY, readbackQuadrants);
}
private static native long nativeGetDrawFnFunctionTable();
private static native long nativeInit();
private static native void nativeSetSurface(long nativeContextManager, Surface surface);
private static native void nativeSync(
long nativeContextManager, int functor, boolean applyForceDark);
private static native int[] nativeDraw(long nativeContextManager, int width, int height,
int scrollX, int scrollY, boolean readbackQuadrants);
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview.shell;
/** draw_fn framework side implementation for tests. */
public class DrawFn {
public static long getDrawFnFunctionTable() {
return nativeGetDrawFnFunctionTable();
}
public static void sync(int functor, boolean applyForceDark) {
nativeSync(functor, applyForceDark);
}
public static void destroyReleased() {
nativeDestroyReleased();
}
public static void drawGL(int functor, int width, int height, int scrollX, int scrollY) {
nativeDrawGL(functor, width, height, scrollX, scrollY);
}
private static native long nativeGetDrawFnFunctionTable();
private static native void nativeSync(int functor, boolean applyForceDark);
private static native void nativeDestroyReleased();
private static native void nativeDrawGL(
int functor, int width, int height, int scrollX, int scrollY);
}
......@@ -9,16 +9,18 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.opengl.GLSurfaceView;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.view.DragEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.EditorInfo;
......@@ -27,21 +29,19 @@ import android.widget.FrameLayout;
import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.gfx.AwDrawFnImpl;
import org.chromium.android_webview.shell.DrawFn;
import org.chromium.android_webview.shell.ContextManager;
import org.chromium.base.Callback;
import org.chromium.content_public.browser.WebContents;
import java.nio.ByteBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
/**
* A View used for testing the AwContents internals.
*
* This class takes the place android.webkit.WebView would have in the production configuration.
*/
public class AwTestContainerView extends FrameLayout {
private static HandlerThread sRenderThread;
private static Handler sRenderThreadHandler;
private AwContents mAwContents;
private AwContents.InternalAccessDelegate mInternalAccessDelegate;
......@@ -50,67 +50,60 @@ public class AwTestContainerView extends FrameLayout {
private Rect mWindowVisibleDisplayFrameOverride;
private class HardwareView extends GLSurfaceView {
private static final int MODE_DRAW = 0;
private static final int MODE_PROCESS = 1;
private static final int MODE_PROCESS_NO_CONTEXT = 2;
private static final int MODE_SYNC = 3;
// mSyncLock is used to synchronized requestRender on the UI thread
// and drawGL on the rendering thread. The variables following
// are protected by it.
private final Object mSyncLock = new Object();
private int mFunctor;
private int mLastDrawnFunctor;
private boolean mSyncDone;
private boolean mPendingDestroy;
private int mLastScrollX;
private int mLastScrollY;
private Callback<int[]> mQuadrantReadbackCallback;
private static final class WaitableEvent {
private final Object mLock = new Object();
private boolean mSignaled;
public void waitForEvent() {
synchronized (mLock) {
while (!mSignaled) {
try {
mLock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
// Only used by drawGL on render thread to store the value of scroll offsets at most recent
// sync for subsequent draws.
private int mCommittedScrollX;
private int mCommittedScrollY;
public void signal() {
synchronized (mLock) {
assert !mSignaled;
mSignaled = true;
mLock.notifyAll();
}
}
}
private class HardwareView extends SurfaceView implements SurfaceHolder.Callback {
// Only accessed on UI thread.
private int mWidth;
private int mHeight;
private int mLastScrollX;
private int mLastScrollY;
private boolean mHaveSurface;
private Runnable mReadyToRenderCallback;
private Runnable mReadyToDetachCallback;
// Only accessed on render thread.
private final ContextManager mContextManager;
public HardwareView(Context context) {
super(context);
setEGLContextClientVersion(2); // GLES2
if (sRenderThread == null) {
sRenderThread = new HandlerThread("RenderThreadInstr");
sRenderThread.start();
sRenderThreadHandler = new Handler(sRenderThread.getLooper());
}
mContextManager = new ContextManager();
getHolder().setFormat(PixelFormat.OPAQUE);
setPreserveEGLContextOnPause(true);
setRenderer(new Renderer() {
private int mWidth;
private int mHeight;
@Override
public void onDrawFrame(GL10 gl) {
HardwareView.this.onDrawFrame(gl, mWidth, mHeight);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
gl.glScissor(0, 0, width, height);
mWidth = width;
mHeight = height;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {}
});
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
getHolder().addCallback(this);
}
public void readbackQuadrantColors(Callback<int[]> callback) {
synchronized (mSyncLock) {
mQuadrantReadbackCallback = callback;
}
super.requestRender();
sRenderThreadHandler.post(() -> {
callback.onResult(mContextManager.draw(
mWidth, mHeight, mLastScrollX, mLastScrollY, /*readbackQuadrants=*/true));
});
}
public boolean isReadyToRender() {
......@@ -122,130 +115,57 @@ public class AwTestContainerView extends FrameLayout {
mReadyToRenderCallback = runner;
}
public void setReadyToDetachCallback(Runnable runner) {
mReadyToDetachCallback = runner;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {}
@Override
public void surfaceCreated(SurfaceHolder holder) {
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mWidth = width;
mHeight = height;
mHaveSurface = true;
Surface surface = holder.getSurface();
sRenderThreadHandler.post(() -> { mContextManager.setSurface(surface); });
if (mReadyToRenderCallback != null) {
mReadyToRenderCallback.run();
mReadyToRenderCallback = null;
}
super.surfaceCreated(holder);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mHaveSurface = false;
if (mReadyToDetachCallback != null) {
mReadyToDetachCallback.run();
mReadyToDetachCallback = null;
}
super.surfaceDestroyed(holder);
WaitableEvent event = new WaitableEvent();
sRenderThreadHandler.post(() -> {
mContextManager.setSurface(null);
event.signal();
});
event.waitForEvent();
}
public void updateScroll(int x, int y) {
synchronized (mSyncLock) {
mLastScrollX = x;
mLastScrollY = y;
}
}
public void awContentsDetached() {
synchronized (mSyncLock) {
super.requestRender();
assert !mPendingDestroy;
mPendingDestroy = true;
try {
while (!mPendingDestroy) {
mSyncLock.wait();
}
} catch (InterruptedException e) {
// ...
}
}
mLastScrollX = x;
mLastScrollY = y;
}
public void drawWebViewFunctor(int functor) {
synchronized (mSyncLock) {
super.requestRender();
assert mFunctor == 0;
mFunctor = functor;
mSyncDone = false;
try {
while (!mSyncDone) {
mSyncLock.wait();
}
} catch (InterruptedException e) {
// ...
}
if (!mHaveSurface) {
return;
}
}
public void onDrawFrame(GL10 gl, int width, int height) {
int functor;
int scrollX;
int scrollY;
synchronized (mSyncLock) {
scrollX = mLastScrollX;
scrollY = mLastScrollY;
if (mFunctor != 0) {
assert !mSyncDone;
functor = mFunctor;
mLastDrawnFunctor = mFunctor;
mFunctor = 0;
DrawFn.sync(functor, false);
mSyncDone = true;
mSyncLock.notifyAll();
} else {
functor = mLastDrawnFunctor;
if (mPendingDestroy) {
DrawFn.destroyReleased();
mPendingDestroy = false;
mLastDrawnFunctor = 0;
mSyncLock.notifyAll();
return;
}
}
}
if (functor != 0) {
DrawFn.drawGL(functor, width, height, scrollX, scrollY);
Callback<int[]> quadrantReadbackCallback = null;
synchronized (mSyncLock) {
if (mQuadrantReadbackCallback != null) {
quadrantReadbackCallback = mQuadrantReadbackCallback;
mQuadrantReadbackCallback = null;
}
}
if (quadrantReadbackCallback != null) {
int quadrantColors[] = new int[4];
int quarterWidth = width / 4;
int quarterHeight = height / 4;
ByteBuffer buffer = ByteBuffer.allocate(4);
gl.glReadPixels(quarterWidth, quarterHeight * 3, 1, 1, GL10.GL_RGBA,
GL10.GL_UNSIGNED_BYTE, buffer);
quadrantColors[0] = readbackToColor(buffer);
gl.glReadPixels(quarterWidth * 3, quarterHeight * 3, 1, 1, GL10.GL_RGBA,
GL10.GL_UNSIGNED_BYTE, buffer);
quadrantColors[1] = readbackToColor(buffer);
gl.glReadPixels(quarterWidth, quarterHeight, 1, 1, GL10.GL_RGBA,
GL10.GL_UNSIGNED_BYTE, buffer);
quadrantColors[2] = readbackToColor(buffer);
gl.glReadPixels(quarterWidth * 3, quarterHeight, 1, 1, GL10.GL_RGBA,
GL10.GL_UNSIGNED_BYTE, buffer);
quadrantColors[3] = readbackToColor(buffer);
quadrantReadbackCallback.onResult(quadrantColors);
}
}
WaitableEvent syncEvent = new WaitableEvent();
sRenderThreadHandler.post(() -> {
drawOnRt(syncEvent, functor, mWidth, mHeight, mLastScrollX, mLastScrollY);
});
syncEvent.waitForEvent();
}
private int readbackToColor(ByteBuffer buffer) {
return Color.argb(buffer.get(3) & 0xff, buffer.get(0) & 0xff, buffer.get(1) & 0xff,
buffer.get(2) & 0xff);
private void drawOnRt(WaitableEvent syncEvent, int functor, int width, int height,
int scrollX, int scrollY) {
mContextManager.sync(functor, false);
syncEvent.signal();
mContextManager.draw(width, height, scrollX, scrollY, /*readbackQuadrants=*/false);
}
}
......@@ -278,7 +198,7 @@ public class AwTestContainerView extends FrameLayout {
public void initialize(AwContents awContents) {
mAwContents = awContents;
if (isBackedByHardwareView()) {
AwDrawFnImpl.setDrawFnFunctionTable(DrawFn.getDrawFnFunctionTable());
AwDrawFnImpl.setDrawFnFunctionTable(ContextManager.getDrawFnFunctionTable());
}
}
......@@ -339,15 +259,6 @@ public class AwTestContainerView extends FrameLayout {
mAttachedContents = true;
}
private void detachedContentsInternal() {
assert mAttachedContents;
mAwContents.onDetachedFromWindow();
mAttachedContents = false;
if (mHardwareView != null) {
mHardwareView.awContentsDetached();
}
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
......@@ -356,22 +267,14 @@ public class AwTestContainerView extends FrameLayout {
} else {
mHardwareView.setReadyToRenderCallback(() -> attachedContentsInternal());
}
if (mHardwareView != null) {
mHardwareView.setReadyToDetachCallback(() -> detachedContentsInternal());
}
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mHardwareView == null || mHardwareView.isReadyToRender()) {
detachedContentsInternal();
if (mHardwareView != null) {
mHardwareView.setReadyToRenderCallback(null);
mHardwareView.setReadyToDetachCallback(null);
}
if (mAttachedContents) {
mAwContents.onDetachedFromWindow();
mAttachedContents = false;
}
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment