// Copyright 2020 The Chromium Authors
// 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 <EGL/egl.h>
#include <GLES2/gl2.h>
#include "android_webview/public/browser/draw_fn.h"
#include "android_webview/test/shell/src/draw_fn/allocator.h"
#include "base/android/jni_array.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/native_library.h"
#include "base/threading/thread_restrictions.h"
#include "gpu/vulkan/init/skia_vk_memory_allocator_impl.h"
#include "gpu/vulkan/init/vulkan_factory.h"
#include "gpu/vulkan/vulkan_device_queue.h"
#include "gpu/vulkan/vulkan_function_pointers.h"
#include "gpu/vulkan/vulkan_implementation.h"
#include "gpu/vulkan/vulkan_instance.h"
#include "gpu/vulkan/vulkan_surface.h"
#include "gpu/vulkan/vulkan_swap_chain.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkDrawable.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkSurfaceProps.h"
#include "third_party/skia/include/gpu/GrBackendSemaphore.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
#include "third_party/skia/include/gpu/GrDirectContext.h"
#include "third_party/skia/include/gpu/GrTypes.h"
#include "third_party/skia/include/gpu/MutableTextureState.h"
#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
#include "third_party/skia/include/gpu/ganesh/vk/GrBackendDrawableInfo.h"
#include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSemaphore.h"
#include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
#include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h"
#include "third_party/skia/include/gpu/vk/GrVkTypes.h"
#include "third_party/skia/include/gpu/vk/VulkanBackendContext.h"
#include "third_party/skia/include/gpu/vk/VulkanExtensions.h"
#include "third_party/skia/include/gpu/vk/VulkanMutableTextureState.h"
#include "third_party/skia/include/gpu/vk/VulkanTypes.h"
#include "ui/gfx/color_space.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "android_webview/test/draw_fn_impl_jni_headers/ContextManager_jni.h"
namespace draw_fn {
namespace {
template <typename T>
void SetColorSpace(T* params) {
// 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;
}
class ContextManagerGL : public ContextManager {
// TODO(penghuang): remove those proc types when EGL header is updated to 1.5.
typedef EGLBoolean(EGLAPIENTRYP PFNEGLINITIALIZEPROC)(EGLDisplay dpy,
EGLint* major,
EGLint* minor);
typedef EGLBoolean(EGLAPIENTRYP PFNEGLCHOOSECONFIGPROC)(
EGLDisplay dpy,
const EGLint* attrib_list,
EGLConfig* configs,
EGLint config_size,
EGLint* num_config);
typedef EGLContext(EGLAPIENTRYP PFNEGLCREATECONTEXTPROC)(
EGLDisplay dpy,
EGLConfig config,
EGLContext share_context,
const EGLint* attrib_list);
typedef EGLSurface(EGLAPIENTRYP PFNEGLCREATEWINDOWSURFACEPROC)(
EGLDisplay dpy,
EGLConfig config,
EGLNativeWindowType win,
const EGLint* attrib_list);
typedef EGLBoolean(EGLAPIENTRYP PFNEGLDESTROYCONTEXTPROC)(EGLDisplay dpy,
EGLContext ctx);
typedef EGLBoolean(EGLAPIENTRYP PFNEGLDESTROYSURFACEPROC)(EGLDisplay dpy,
EGLSurface surface);
typedef EGLDisplay(EGLAPIENTRYP PFNEGLGETDISPLAYPROC)(
EGLNativeDisplayType display_id);
typedef __eglMustCastToProperFunctionPointerType(
EGLAPIENTRYP PFNEGLGETPROCADDRESSPROC)(const char* procname);
typedef EGLBoolean(EGLAPIENTRYP PFNEGLMAKECURRENTPROC)(EGLDisplay dpy,
EGLSurface draw,
EGLSurface read,
EGLContext ctx);
typedef EGLBoolean(EGLAPIENTRYP PFNEGLSWAPBUFFERSPROC)(EGLDisplay dpy,
EGLSurface surface);
typedef EGLBoolean(EGLAPIENTRYP PFNEGLBINDAPIPROC)(EGLenum api);
// These bindings could be static, but ContextManager is effectively a
// singleton so just keeping them as member variables / functions.
PFNEGLGETPROCADDRESSPROC eglGetProcAddressFn = nullptr;
PFNEGLBINDAPIPROC eglBindAPIFn = nullptr;
PFNEGLINITIALIZEPROC eglInitialize = nullptr;
PFNEGLGETDISPLAYPROC eglGetDisplayFn = nullptr;
PFNEGLMAKECURRENTPROC eglMakeCurrentFn = nullptr;
PFNEGLSWAPBUFFERSPROC eglSwapBuffersFn = nullptr;
PFNEGLCHOOSECONFIGPROC eglChooseConfigFn = nullptr;
PFNEGLCREATECONTEXTPROC eglCreateContextFn = nullptr;
PFNEGLDESTROYCONTEXTPROC eglDestroyContextFn = nullptr;
PFNEGLCREATEWINDOWSURFACEPROC eglCreateWindowSurfaceFn = nullptr;
PFNEGLDESTROYSURFACEPROC eglDestroySurfaceFn = nullptr;
PFNGLREADPIXELSPROC glReadPixelsFn = nullptr;
template <typename T>
void AssignProc(T& fn, const char* name) {
fn = reinterpret_cast<T>(eglGetProcAddressFn(name));
CHECK(fn) << "Failed to get " << name;
}
void InitializeGLBindings() {
if (eglGetProcAddressFn)
return;
base::ScopedAllowBlockingForTesting allow_blocking;
base::NativeLibraryLoadError error;
base::FilePath filename("libEGL.so");
base::NativeLibrary egl_library = base::LoadNativeLibrary(filename, &error);
CHECK(egl_library) << "Failed to load " << filename.MaybeAsASCII() << ": "
<< error.ToString();
eglGetProcAddressFn = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(
base::GetFunctionPointerFromNativeLibrary(egl_library,
"eglGetProcAddress"));
CHECK(eglGetProcAddressFn) << "Failed to get eglGetProcAddress.";
AssignProc(eglBindAPIFn, "eglBindAPI");
AssignProc(eglInitialize, "eglInitialize");
AssignProc(eglGetDisplayFn, "eglGetDisplay");
AssignProc(eglMakeCurrentFn, "eglMakeCurrent");
AssignProc(eglSwapBuffersFn, "eglSwapBuffers");
AssignProc(eglChooseConfigFn, "eglChooseConfig");
AssignProc(eglCreateContextFn, "eglCreateContext");
AssignProc(eglDestroyContextFn, "eglDestroyContext");
AssignProc(eglCreateWindowSurfaceFn, "eglCreateWindowSurface");
AssignProc(eglDestroySurfaceFn, "eglDestroySurface");
AssignProc(glReadPixelsFn, "glReadPixels");
}
EGLDisplay GetDisplay() {
static EGLDisplay display = nullptr;
if (!display) {
display = eglGetDisplayFn(EGL_DEFAULT_DISPLAY);
CHECK_NE(display, EGL_NO_DISPLAY);
CHECK(eglInitialize(display, nullptr, nullptr));
}
return display;
}
int rgbaToArgb(GLubyte* bytes) {
return (bytes[3] & 0xff) << 24 | (bytes[0] & 0xff) << 16 |
(bytes[1] & 0xff) << 8 | (bytes[2] & 0xff);
}
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 (!eglChooseConfigFn(GetDisplay(), config_attribs, nullptr, 0,
&num_configs) ||
num_configs == 0) {
continue;
}
CHECK(eglChooseConfigFn(GetDisplay(), config_attribs, &config, 1,
&num_configs));
use_es3 = try_es3;
break;
}
CHECK(config);
*out_use_es3 = use_es3;
return config;
}
public:
ContextManagerGL();
~ContextManagerGL() override;
void ResizeSurface(JNIEnv* env, int width, int height) override {}
base::android::ScopedJavaLocalRef<jintArray> Draw(
JNIEnv* env,
int width,
int height,
int scroll_x,
int scroll_y,
jboolean readback_quadrants) override;
void DoCreateContext(JNIEnv* env, int width, int height) override;
void DestroyContext() override;
void CurrentFunctorChanged() override {}
private:
void MakeCurrent();
EGLSurface gl_surface_ = nullptr;
EGLContext gl_context_ = nullptr;
};
ContextManagerGL::ContextManagerGL() {
InitializeGLBindings();
}
ContextManagerGL::~ContextManagerGL() {
DestroyContext();
}
base::android::ScopedJavaLocalRef<jintArray> ContextManagerGL::Draw(
JNIEnv* env,
int width,
int height,
int scroll_x,
int scroll_y,
jboolean readback_quadrants) {
int results[] = {0, 0, 0, 0};
if (!current_functor_ || !gl_context_) {
LOG(ERROR) << "Draw failed. context:" << gl_context_
<< " functor:" << current_functor_;
return readback_quadrants ? base::android::ToJavaIntArray(env, results)
: nullptr;
}
MakeCurrent();
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;
SetColorSpace(¶ms);
FunctorData& data = Allocator::Get()->get(current_functor_);
OverlaysManager::ScopedDraw scoped_draw(overlays_manager_, data, params);
data.functor_callbacks->draw_gl(current_functor_, data.data, ¶ms);
if (readback_quadrants) {
int quarter_width = width / 4;
int quarter_height = height / 4;
GLubyte bytes[4] = {};
glReadPixelsFn(quarter_width, quarter_height * 3, 1, 1, GL_RGBA,
GL_UNSIGNED_BYTE, bytes);
results[0] = rgbaToArgb(bytes);
glReadPixelsFn(quarter_width * 3, quarter_height * 3, 1, 1, GL_RGBA,
GL_UNSIGNED_BYTE, bytes);
results[1] = rgbaToArgb(bytes);
glReadPixelsFn(quarter_width, quarter_height, 1, 1, GL_RGBA,
GL_UNSIGNED_BYTE, bytes);
results[2] = rgbaToArgb(bytes);
glReadPixelsFn(quarter_width * 3, quarter_height, 1, 1, GL_RGBA,
GL_UNSIGNED_BYTE, bytes);
results[3] = rgbaToArgb(bytes);
}
CHECK(eglSwapBuffersFn(GetDisplay(), gl_surface_));
return readback_quadrants ? base::android::ToJavaIntArray(env, results)
: nullptr;
}
void ContextManagerGL::DoCreateContext(JNIEnv* env, int width, int height) {
bool use_es3 = false;
{
std::vector<EGLint> egl_window_attributes;
egl_window_attributes.push_back(EGL_NONE);
gl_surface_ = eglCreateWindowSurfaceFn(GetDisplay(), GetConfig(&use_es3),
native_window_.a_native_window(),
&egl_window_attributes[0]);
CHECK(gl_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(eglBindAPIFn(EGL_OPENGL_ES_API));
gl_context_ = eglCreateContextFn(GetDisplay(), GetConfig(&use_es3), nullptr,
context_attributes.data());
CHECK(gl_context_);
}
return;
}
void ContextManagerGL::DestroyContext() {
if (java_surface_.IsEmpty()) {
return;
}
if (current_functor_) {
MakeCurrent();
FunctorData& data = Allocator::Get()->get(current_functor_);
overlays_manager_.RemoveOverlays(data);
data.functor_callbacks->on_context_destroyed(data.functor, data.data);
}
DCHECK(gl_context_);
CHECK(eglDestroyContextFn(GetDisplay(), gl_context_));
gl_context_ = nullptr;
DCHECK(gl_surface_);
CHECK(eglDestroySurfaceFn(GetDisplay(), gl_surface_));
gl_surface_ = nullptr;
native_window_ = nullptr;
java_surface_ = nullptr;
}
void ContextManagerGL::MakeCurrent() {
DCHECK(gl_surface_);
DCHECK(gl_context_);
CHECK(eglMakeCurrentFn(GetDisplay(), gl_surface_, gl_surface_, gl_context_));
}
class VkFunctorDrawHandler : public SkDrawable::GpuDrawHandler {
public:
VkFunctorDrawHandler(OverlaysManager* overlays_manager,
int functor,
int scroll_x,
int scroll_y,
const SkMatrix& matrix,
const SkIRect& clip_bounds,
const SkImageInfo& image_info)
: overlays_manager_(overlays_manager),
functor_(functor),
scroll_x_(scroll_x),
scroll_y_(scroll_y),
matrix_(matrix),
clip_bounds_(clip_bounds),
image_info_(image_info) {}
~VkFunctorDrawHandler() override {
AwDrawFn_PostDrawVkParams params{kAwDrawFnVersion};
FunctorData& data = Allocator::Get()->get(functor_);
data.functor_callbacks->post_draw_vk(functor_, data.data, ¶ms);
}
void draw(const GrBackendDrawableInfo& backend_drawable_info) override {
GrVkDrawableInfo vulkan_info;
CHECK(backend_drawable_info.getVkDrawableInfo(&vulkan_info));
AwDrawFn_DrawVkParams params{kAwDrawFnVersion};
params.width = image_info_.width();
params.height = image_info_.height();
SkM44 mat4(matrix_);
mat4.postTranslate(-scroll_x_, -scroll_y_);
mat4.getColMajor(¶ms.transform[0]);
params.secondary_command_buffer = vulkan_info.fSecondaryCommandBuffer;
params.color_attachment_index = vulkan_info.fColorAttachmentIndex;
params.compatible_render_pass = vulkan_info.fCompatibleRenderPass;
params.format = vulkan_info.fFormat;
params.clip_left = clip_bounds_.fLeft;
params.clip_top = clip_bounds_.fTop;
params.clip_right = clip_bounds_.fRight;
params.clip_bottom = clip_bounds_.fBottom;
SetColorSpace(¶ms);
FunctorData& data = Allocator::Get()->get(functor_);
OverlaysManager::ScopedDraw scoped_draw(*overlays_manager_, data, params);
data.functor_callbacks->draw_vk(functor_, data.data, ¶ms);
}
private:
raw_ptr<OverlaysManager> overlays_manager_;
int functor_;
int scroll_x_;
int scroll_y_;
const SkMatrix matrix_;
const SkIRect clip_bounds_;
const SkImageInfo image_info_;
};
class FunctorDrawable : public SkDrawable {
public:
FunctorDrawable(OverlaysManager* overlays_manager,
int functor,
int scroll_x,
int scroll_y,
int width,
int height)
: overlays_manager_(overlays_manager),
functor_(functor),
scroll_x_(scroll_x),
scroll_y_(scroll_y),
width_(width),
height_(height) {}
protected:
SkRect onGetBounds() override { return SkRect::MakeWH(width_, height_); }
void onDraw(SkCanvas*) override { NOTREACHED(); }
std::unique_ptr<GpuDrawHandler> onSnapGpuDrawHandler(
GrBackendApi backend_api,
const SkMatrix& matrix,
const SkIRect& clip_bounds,
const SkImageInfo& image_info) override {
CHECK_EQ(backend_api, GrBackendApi::kVulkan);
return std::make_unique<VkFunctorDrawHandler>(overlays_manager_, functor_,
scroll_x_, scroll_y_, matrix,
clip_bounds, image_info);
}
private:
raw_ptr<OverlaysManager> overlays_manager_;
int functor_;
int scroll_x_;
int scroll_y_;
int width_;
int height_;
};
class ContextManagerVulkan : public ContextManager {
public:
ContextManagerVulkan();
~ContextManagerVulkan() override;
void ResizeSurface(JNIEnv* env, int width, int height) override;
base::android::ScopedJavaLocalRef<jintArray> Draw(
JNIEnv* env,
int width,
int height,
int scroll_x,
int scroll_y,
jboolean readback_quadrants) override;
void DoCreateContext(JNIEnv* env, int width, int height) override;
void DestroyContext() override;
void CurrentFunctorChanged() override;
private:
void MaybeCallFunctorInitVk();
std::unique_ptr<gpu::VulkanImplementation> vulkan_implementation_;
std::unique_ptr<gpu::VulkanDeviceQueue> device_queue_;
std::unique_ptr<gpu::VulkanSurface> vulkan_surface_;
sk_sp<GrDirectContext> gr_context_;
std::vector<sk_sp<SkSurface>> sk_surfaces_;
};
ContextManagerVulkan::ContextManagerVulkan() {
base::ScopedAllowBlockingForTesting allow_blocking;
vulkan_implementation_ = gpu::CreateVulkanImplementation(
/*use_swiftshader=*/false,
/*allow_protected_memory*/ false);
CHECK(vulkan_implementation_);
CHECK(
vulkan_implementation_->InitializeVulkanInstance(/*using_surface=*/true));
uint32_t flags = gpu::VulkanDeviceQueue::GRAPHICS_QUEUE_FLAG |
gpu::VulkanDeviceQueue::PRESENTATION_SUPPORT_QUEUE_FLAG;
device_queue_ =
gpu::CreateVulkanDeviceQueue(vulkan_implementation_.get(), flags,
/*gpu_info=*/nullptr);
CHECK(device_queue_);
}
ContextManagerVulkan::~ContextManagerVulkan() {
DestroyContext();
}
void ContextManagerVulkan::ResizeSurface(JNIEnv* env, int width, int height) {
DCHECK(vulkan_surface_);
CHECK(vulkan_surface_->Reshape(
gfx::Size(width, height), gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE));
sk_surfaces_.clear();
sk_surfaces_.resize(vulkan_surface_->swap_chain()->num_images());
}
base::android::ScopedJavaLocalRef<jintArray> ContextManagerVulkan::Draw(
JNIEnv* env,
int width,
int height,
int scroll_x,
int scroll_y,
jboolean readback_quadrants) {
int results[] = {0, 0, 0, 0};
if (!current_functor_) {
LOG(ERROR) << "Draw failed no functor:" << current_functor_;
return readback_quadrants ? base::android::ToJavaIntArray(env, results)
: nullptr;
}
{
gpu::VulkanSwapChain::ScopedWrite scoped_write(
vulkan_surface_->swap_chain());
CHECK(scoped_write.success());
auto& sk_surface = sk_surfaces_[scoped_write.image_index()];
if (!sk_surface) {
SkSurfaceProps surface_props(0, kRGB_H_SkPixelGeometry);
const auto surface_format = vulkan_surface_->surface_format().format;
DCHECK(surface_format == VK_FORMAT_B8G8R8A8_UNORM ||
surface_format == VK_FORMAT_R8G8B8A8_UNORM);
GrVkImageInfo vk_image_info;
vk_image_info.fImage = scoped_write.image();
vk_image_info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
vk_image_info.fImageLayout = scoped_write.image_layout();
vk_image_info.fFormat = surface_format;
vk_image_info.fImageUsageFlags = scoped_write.image_usage();
vk_image_info.fSampleCount = 1;
vk_image_info.fLevelCount = 1;
vk_image_info.fCurrentQueueFamily = VK_QUEUE_FAMILY_IGNORED;
vk_image_info.fProtected = GrProtected::kNo;
const auto& vk_image_size = vulkan_surface_->image_size();
auto render_target = GrBackendRenderTargets::MakeVk(
vk_image_size.width(), vk_image_size.height(), vk_image_info);
auto sk_color_type = surface_format == VK_FORMAT_B8G8R8A8_UNORM
? kBGRA_8888_SkColorType
: kRGBA_8888_SkColorType;
sk_surface = SkSurfaces::WrapBackendRenderTarget(
gr_context_.get(), render_target, kTopLeft_GrSurfaceOrigin,
sk_color_type, gfx::ColorSpace::CreateSRGB().ToSkColorSpace(),
&surface_props);
CHECK(sk_surface);
} else {
auto backend = SkSurfaces::GetBackendRenderTarget(
sk_surface.get(), SkSurfaces::BackendHandleAccess::kFlushRead);
GrBackendRenderTargets::SetVkImageLayout(&backend,
scoped_write.image_layout());
}
{
VkSemaphore vk_semaphore = scoped_write.begin_semaphore();
DCHECK(vk_semaphore != VK_NULL_HANDLE);
GrBackendSemaphore begin_semaphore =
GrBackendSemaphores::MakeVk(vk_semaphore);
bool result = sk_surface->wait(1, &begin_semaphore,
/*deleteSemaphoresAfterWait=*/false);
CHECK(result);
}
auto functor_drawable = sk_make_sp<FunctorDrawable>(
&overlays_manager_, current_functor_, scroll_x, scroll_y,
vulkan_surface_->image_size().width(),
vulkan_surface_->image_size().height());
sk_surface->getCanvas()->drawDrawable(functor_drawable.get());
if (readback_quadrants) {
int quarter_width = width / 4;
int quarter_height = height / 4;
SkBitmap bitmap;
bitmap.allocN32Pixels(1, 1);
CHECK(sk_surface->readPixels(bitmap, quarter_width, quarter_height));
results[0] = bitmap.getColor(0, 0);
CHECK(sk_surface->readPixels(bitmap, quarter_width * 3, quarter_height));
results[1] = bitmap.getColor(0, 0);
CHECK(sk_surface->readPixels(bitmap, quarter_width, quarter_height * 3));
results[2] = bitmap.getColor(0, 0);
CHECK(sk_surface->readPixels(bitmap, quarter_width * 3,
quarter_height * 3));
results[3] = bitmap.getColor(0, 0);
}
{
GrBackendSemaphore end_semaphore =
GrBackendSemaphores::MakeVk(scoped_write.end_semaphore());
GrFlushInfo flush_info = {
.fNumSemaphores = 1,
.fSignalSemaphores = &end_semaphore,
};
uint32_t queue_index = device_queue_->GetVulkanQueueIndex();
skgpu::MutableTextureState state =
skgpu::MutableTextureStates::MakeVulkan(
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, queue_index);
GrSemaphoresSubmitted submitted =
gr_context_->flush(sk_surface.get(), flush_info, &state);
CHECK_EQ(GrSemaphoresSubmitted::kYes, submitted);
}
CHECK(gr_context_->submit(GrSyncCpu::kNo));
}
gfx::SwapResult result = vulkan_surface_->SwapBuffers(
base::DoNothingAs<void(const gfx::PresentationFeedback&)>());
CHECK_EQ(gfx::SwapResult::SWAP_ACK, result);
return readback_quadrants ? base::android::ToJavaIntArray(env, results)
: nullptr;
}
void ContextManagerVulkan::DoCreateContext(JNIEnv* env, int width, int height) {
vulkan_surface_ = vulkan_implementation_->CreateViewSurface(
native_window_.a_native_window());
CHECK(vulkan_surface_);
CHECK(vulkan_surface_->Initialize(device_queue_.get(),
gpu::VulkanSurface::FORMAT_RGBA_32));
ResizeSurface(env, width, height);
skgpu::VulkanBackendContext backend_context;
backend_context.fInstance = device_queue_->GetVulkanInstance();
backend_context.fPhysicalDevice = device_queue_->GetVulkanPhysicalDevice();
backend_context.fDevice = device_queue_->GetVulkanDevice();
backend_context.fQueue = device_queue_->GetVulkanQueue();
backend_context.fGraphicsQueueIndex = device_queue_->GetVulkanQueueIndex();
backend_context.fMaxAPIVersion = vulkan_implementation_->GetVulkanInstance()
->vulkan_info()
.used_api_version;
backend_context.fMemoryAllocator =
gpu::CreateSkiaVulkanMemoryAllocator(device_queue_.get());
skgpu::VulkanGetProc get_proc = [](const char* proc_name, VkInstance instance,
VkDevice device) {
if (device) {
return vkGetDeviceProcAddr(device, proc_name);
}
return vkGetInstanceProcAddr(instance, proc_name);
};
const auto& instance_extensions = vulkan_implementation_->GetVulkanInstance()
->vulkan_info()
.enabled_instance_extensions;
std::vector<const char*> device_extensions;
device_extensions.reserve(device_queue_->enabled_extensions().size());
for (const auto& extension : device_queue_->enabled_extensions())
device_extensions.push_back(extension.data());
skgpu::VulkanExtensions vk_extensions;
vk_extensions.init(get_proc,
vulkan_implementation_->GetVulkanInstance()->vk_instance(),
device_queue_->GetVulkanPhysicalDevice(),
instance_extensions.size(), instance_extensions.data(),
device_extensions.size(), device_extensions.data());
backend_context.fVkExtensions = &vk_extensions;
backend_context.fDeviceFeatures2 =
&device_queue_->enabled_device_features_2();
backend_context.fGetProc = get_proc;
backend_context.fProtectedContext = GrProtected::kNo;
GrContextOptions options;
gr_context_ = GrDirectContexts::MakeVulkan(backend_context, options);
CHECK(gr_context_);
MaybeCallFunctorInitVk();
}
void ContextManagerVulkan::DestroyContext() {
if (java_surface_.IsEmpty()) {
return;
}
if (current_functor_) {
FunctorData& data = Allocator::Get()->get(current_functor_);
data.functor_callbacks->on_context_destroyed(data.functor, data.data);
}
sk_surfaces_.clear();
vulkan_surface_->Finish();
vulkan_surface_->Destroy();
vulkan_surface_.reset();
native_window_ = nullptr;
java_surface_ = nullptr;
}
void ContextManagerVulkan::CurrentFunctorChanged() {
MaybeCallFunctorInitVk();
}
void ContextManagerVulkan::MaybeCallFunctorInitVk() {
if (!vulkan_surface_ || !current_functor_)
return;
AwDrawFn_InitVkParams params{kAwDrawFnVersion};
params.instance = device_queue_->GetVulkanInstance();
params.physical_device = device_queue_->GetVulkanPhysicalDevice();
params.device = device_queue_->GetVulkanDevice();
params.queue = device_queue_->GetVulkanQueue();
params.graphics_queue_index = device_queue_->GetVulkanQueueIndex();
const gpu::VulkanInfo& vulkan_info =
vulkan_implementation_->GetVulkanInstance()->vulkan_info();
params.api_version = vulkan_info.used_api_version;
params.enabled_instance_extension_names =
vulkan_info.enabled_instance_extensions.data();
params.enabled_instance_extension_names_length =
vulkan_info.enabled_instance_extensions.size();
std::vector<const char*> enabled_device_extension_names;
for (const auto& extension_string_piece :
device_queue_->enabled_extensions()) {
enabled_device_extension_names.push_back(extension_string_piece.data());
}
params.enabled_device_extension_names = enabled_device_extension_names.data();
params.enabled_device_extension_names_length =
enabled_device_extension_names.size();
VkPhysicalDeviceFeatures2 device_features_2 =
device_queue_->enabled_device_features_2();
params.device_features_2 = &device_features_2;
FunctorData& data = Allocator::Get()->get(current_functor_);
data.functor_callbacks->init_vk(data.functor, data.data, ¶ms);
}
} // namespace
ContextManager::ContextManager() = default;
ContextManager::~ContextManager() = default;
void ContextManager::SetSurface(JNIEnv* env,
const base::android::JavaRef<jobject>& surface,
int width,
int height) {
if (!java_surface_.IsEmpty()) {
DestroyContext();
}
if (!surface.is_null()) {
CreateContext(env, surface, width, height);
}
}
void ContextManager::SetOverlaysSurface(
JNIEnv* env,
const base::android::JavaRef<jobject>& surface) {
overlays_manager_.SetSurface(current_functor_, env, surface);
}
void ContextManager::Sync(JNIEnv* env, int functor, bool apply_force_dark) {
bool functor_changing = current_functor_ != functor;
if (current_functor_ && functor_changing) {
FunctorData& data = Allocator::Get()->get(current_functor_);
overlays_manager_.RemoveOverlays(data);
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, ¶ms);
if (functor_changing)
CurrentFunctorChanged();
}
void ContextManager::CreateContext(
JNIEnv* env,
const base::android::JavaRef<jobject>& surface,
int width,
int height) {
java_surface_ = gl::ScopedJavaSurface(surface, /*auto_release=*/false);
if (!java_surface_.IsValid()) {
return;
}
native_window_ = gl::ScopedANativeWindow(java_surface_);
CHECK(native_window_);
DoCreateContext(env, width, height);
}
static jlong JNI_ContextManager_GetDrawFnFunctionTable(JNIEnv* env,
jboolean use_vulkan) {
draw_fn::SetDrawFnUseVulkan(use_vulkan);
return reinterpret_cast<intptr_t>(draw_fn::GetDrawFnFunctionTable());
}
static jlong JNI_ContextManager_Init(JNIEnv* env, jboolean use_vulkan) {
ContextManager* manager = nullptr;
if (use_vulkan) {
manager = new draw_fn::ContextManagerVulkan;
} else {
manager = new draw_fn::ContextManagerGL;
}
return reinterpret_cast<intptr_t>(manager);
}
} // namespace draw_fn