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

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: default avatarJonathan Backer <backer@chromium.org>
Reviewed-by: default avatarVasiliy Telezhnikov <vasilyt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#837094}
parent dedda4d3
...@@ -74,7 +74,7 @@ android_apk("webview_instrumentation_apk") { ...@@ -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/AwShellApplication.java",
"shell/src/org/chromium/android_webview/shell/AwShellResourceProvider.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/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/AwJUnit4ClassRunner.java",
"shell/src/org/chromium/android_webview/test/AwTestContainerView.java", "shell/src/org/chromium/android_webview/test/AwTestContainerView.java",
"shell/src/org/chromium/android_webview/test/AwTestRunnerActivity.java", "shell/src/org/chromium/android_webview/test/AwTestRunnerActivity.java",
...@@ -137,12 +137,24 @@ android_assets("webview_instrumentation_apk_assets") { ...@@ -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") { shared_library("libstandalonelibwebviewchromium") {
testonly = true 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" ] ldflags = [ "-Wl,-shared,-Bsymbolic" ]
deps = [ deps = [
":draw_fn_impl_jni_headers",
":webview_instrumentation_test_native_jni_impl", ":webview_instrumentation_test_native_jni_impl",
"//android_webview/browser/gfx",
"//android_webview/lib", "//android_webview/lib",
"//android_webview/lib:webview_entry_point", "//android_webview/lib:webview_entry_point",
"//android_webview/nonembedded", "//android_webview/nonembedded",
...@@ -151,7 +163,9 @@ shared_library("libstandalonelibwebviewchromium") { ...@@ -151,7 +163,9 @@ shared_library("libstandalonelibwebviewchromium") {
"//components/autofill/android/provider/test_support:component_autofill_provider_native_test_support", "//components/autofill/android/provider/test_support:component_autofill_provider_native_test_support",
"//components/heap_profiling/multi_process:test_support", "//components/heap_profiling/multi_process:test_support",
"//content/public/test/android:content_native_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_onload" ]
configs += [ "//build/config/android:hide_all_but_jni" ] 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 {
public static long getDrawFnFunctionTable() {
return nativeGetDrawFnFunctionTable();
}
private final long mNativeContextManager;
public ContextManager() {
mNativeContextManager = nativeInit();
}
public void setSurface(Surface 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; ...@@ -9,16 +9,18 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.graphics.Rect; import android.graphics.Rect;
import android.opengl.GLSurfaceView;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.view.DragEvent; import android.view.DragEvent;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View; import android.view.View;
import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.AccessibilityNodeProvider;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
...@@ -27,21 +29,19 @@ import android.widget.FrameLayout; ...@@ -27,21 +29,19 @@ import android.widget.FrameLayout;
import org.chromium.android_webview.AwContents; import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.gfx.AwDrawFnImpl; 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.base.Callback;
import org.chromium.content_public.browser.WebContents; 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. * A View used for testing the AwContents internals.
* *
* This class takes the place android.webkit.WebView would have in the production configuration. * This class takes the place android.webkit.WebView would have in the production configuration.
*/ */
public class AwTestContainerView extends FrameLayout { public class AwTestContainerView extends FrameLayout {
private static HandlerThread sRenderThread;
private static Handler sRenderThreadHandler;
private AwContents mAwContents; private AwContents mAwContents;
private AwContents.InternalAccessDelegate mInternalAccessDelegate; private AwContents.InternalAccessDelegate mInternalAccessDelegate;
...@@ -50,67 +50,60 @@ public class AwTestContainerView extends FrameLayout { ...@@ -50,67 +50,60 @@ public class AwTestContainerView extends FrameLayout {
private Rect mWindowVisibleDisplayFrameOverride; private Rect mWindowVisibleDisplayFrameOverride;
private class HardwareView extends GLSurfaceView { private static final class WaitableEvent {
private static final int MODE_DRAW = 0; private final Object mLock = new Object();
private static final int MODE_PROCESS = 1; private boolean mSignaled;
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;
// Only used by drawGL on render thread to store the value of scroll offsets at most recent public void waitForEvent() {
// sync for subsequent draws. synchronized (mLock) {
private int mCommittedScrollX; while (!mSignaled) {
private int mCommittedScrollY; try {
mLock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
private boolean mHaveSurface; public void signal() {
private Runnable mReadyToRenderCallback; synchronized (mLock) {
private Runnable mReadyToDetachCallback; assert !mSignaled;
mSignaled = true;
mLock.notifyAll();
}
}
}
public HardwareView(Context context) { private class HardwareView extends SurfaceView implements SurfaceHolder.Callback {
super(context); // Only accessed on UI thread.
setEGLContextClientVersion(2); // GLES2
getHolder().setFormat(PixelFormat.OPAQUE);
setPreserveEGLContextOnPause(true);
setRenderer(new Renderer() {
private int mWidth; private int mWidth;
private int mHeight; private int mHeight;
private int mLastScrollX;
private int mLastScrollY;
private boolean mHaveSurface;
private Runnable mReadyToRenderCallback;
@Override // Only accessed on render thread.
public void onDrawFrame(GL10 gl) { private final ContextManager mContextManager;
HardwareView.this.onDrawFrame(gl, mWidth, mHeight);
}
@Override public HardwareView(Context context) {
public void onSurfaceChanged(GL10 gl, int width, int height) { super(context);
gl.glViewport(0, 0, width, height); if (sRenderThread == null) {
gl.glScissor(0, 0, width, height); sRenderThread = new HandlerThread("RenderThreadInstr");
mWidth = width; sRenderThread.start();
mHeight = height; sRenderThreadHandler = new Handler(sRenderThread.getLooper());
} }
mContextManager = new ContextManager();
@Override getHolder().setFormat(PixelFormat.OPAQUE);
public void onSurfaceCreated(GL10 gl, EGLConfig config) {} getHolder().addCallback(this);
});
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
} }
public void readbackQuadrantColors(Callback<int[]> callback) { public void readbackQuadrantColors(Callback<int[]> callback) {
synchronized (mSyncLock) { sRenderThreadHandler.post(() -> {
mQuadrantReadbackCallback = callback; callback.onResult(mContextManager.draw(
} mWidth, mHeight, mLastScrollX, mLastScrollY, /*readbackQuadrants=*/true));
super.requestRender(); });
} }
public boolean isReadyToRender() { public boolean isReadyToRender() {
...@@ -122,130 +115,57 @@ public class AwTestContainerView extends FrameLayout { ...@@ -122,130 +115,57 @@ public class AwTestContainerView extends FrameLayout {
mReadyToRenderCallback = runner; mReadyToRenderCallback = runner;
} }
public void setReadyToDetachCallback(Runnable runner) { @Override
mReadyToDetachCallback = runner; public void surfaceCreated(SurfaceHolder holder) {}
}
@Override @Override
public void surfaceCreated(SurfaceHolder holder) { public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mWidth = width;
mHeight = height;
mHaveSurface = true; mHaveSurface = true;
Surface surface = holder.getSurface();
sRenderThreadHandler.post(() -> { mContextManager.setSurface(surface); });
if (mReadyToRenderCallback != null) { if (mReadyToRenderCallback != null) {
mReadyToRenderCallback.run(); mReadyToRenderCallback.run();
mReadyToRenderCallback = null; mReadyToRenderCallback = null;
} }
super.surfaceCreated(holder);
} }
@Override @Override
public void surfaceDestroyed(SurfaceHolder holder) { public void surfaceDestroyed(SurfaceHolder holder) {
mHaveSurface = false; mHaveSurface = false;
if (mReadyToDetachCallback != null) { WaitableEvent event = new WaitableEvent();
mReadyToDetachCallback.run(); sRenderThreadHandler.post(() -> {
mReadyToDetachCallback = null; mContextManager.setSurface(null);
} event.signal();
super.surfaceDestroyed(holder); });
event.waitForEvent();
} }
public void updateScroll(int x, int y) { public void updateScroll(int x, int y) {
synchronized (mSyncLock) {
mLastScrollX = x; mLastScrollX = x;
mLastScrollY = y; mLastScrollY = y;
} }
}
public void awContentsDetached() {
synchronized (mSyncLock) {
super.requestRender();
assert !mPendingDestroy;
mPendingDestroy = true;
try {
while (!mPendingDestroy) {
mSyncLock.wait();
}
} catch (InterruptedException e) {
// ...
}
}
}
public void drawWebViewFunctor(int functor) { public void drawWebViewFunctor(int functor) {
synchronized (mSyncLock) { if (!mHaveSurface) {
super.requestRender();
assert mFunctor == 0;
mFunctor = functor;
mSyncDone = false;
try {
while (!mSyncDone) {
mSyncLock.wait();
}
} catch (InterruptedException e) {
// ...
}
}
}
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; return;
} }
}
}
if (functor != 0) {
DrawFn.drawGL(functor, width, height, scrollX, scrollY);
Callback<int[]> quadrantReadbackCallback = null; WaitableEvent syncEvent = new WaitableEvent();
synchronized (mSyncLock) { sRenderThreadHandler.post(() -> {
if (mQuadrantReadbackCallback != null) { drawOnRt(syncEvent, functor, mWidth, mHeight, mLastScrollX, mLastScrollY);
quadrantReadbackCallback = mQuadrantReadbackCallback; });
mQuadrantReadbackCallback = null; syncEvent.waitForEvent();
}
}
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);
}
}
} }
private int readbackToColor(ByteBuffer buffer) { private void drawOnRt(WaitableEvent syncEvent, int functor, int width, int height,
return Color.argb(buffer.get(3) & 0xff, buffer.get(0) & 0xff, buffer.get(1) & 0xff, int scrollX, int scrollY) {
buffer.get(2) & 0xff); mContextManager.sync(functor, false);
syncEvent.signal();
mContextManager.draw(width, height, scrollX, scrollY, /*readbackQuadrants=*/false);
} }
} }
...@@ -278,7 +198,7 @@ public class AwTestContainerView extends FrameLayout { ...@@ -278,7 +198,7 @@ public class AwTestContainerView extends FrameLayout {
public void initialize(AwContents awContents) { public void initialize(AwContents awContents) {
mAwContents = awContents; mAwContents = awContents;
if (isBackedByHardwareView()) { if (isBackedByHardwareView()) {
AwDrawFnImpl.setDrawFnFunctionTable(DrawFn.getDrawFnFunctionTable()); AwDrawFnImpl.setDrawFnFunctionTable(ContextManager.getDrawFnFunctionTable());
} }
} }
...@@ -339,15 +259,6 @@ public class AwTestContainerView extends FrameLayout { ...@@ -339,15 +259,6 @@ public class AwTestContainerView extends FrameLayout {
mAttachedContents = true; mAttachedContents = true;
} }
private void detachedContentsInternal() {
assert mAttachedContents;
mAwContents.onDetachedFromWindow();
mAttachedContents = false;
if (mHardwareView != null) {
mHardwareView.awContentsDetached();
}
}
@Override @Override
public void onAttachedToWindow() { public void onAttachedToWindow() {
super.onAttachedToWindow(); super.onAttachedToWindow();
...@@ -356,22 +267,14 @@ public class AwTestContainerView extends FrameLayout { ...@@ -356,22 +267,14 @@ public class AwTestContainerView extends FrameLayout {
} else { } else {
mHardwareView.setReadyToRenderCallback(() -> attachedContentsInternal()); mHardwareView.setReadyToRenderCallback(() -> attachedContentsInternal());
} }
if (mHardwareView != null) {
mHardwareView.setReadyToDetachCallback(() -> detachedContentsInternal());
}
} }
@Override @Override
public void onDetachedFromWindow() { public void onDetachedFromWindow() {
super.onDetachedFromWindow(); super.onDetachedFromWindow();
if (mHardwareView == null || mHardwareView.isReadyToRender()) { if (mAttachedContents) {
detachedContentsInternal(); mAwContents.onDetachedFromWindow();
mAttachedContents = false;
if (mHardwareView != null) {
mHardwareView.setReadyToRenderCallback(null);
mHardwareView.setReadyToDetachCallback(null);
}
} }
} }
......
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