New WebViewFunctor API
Should function alongside existing functor API. Bug: 120997728 Test: hwuiunit passes Change-Id: I8f6143d0be1111431b55016f34de319f6b8c8910
This commit is contained in:
@@ -168,6 +168,11 @@ static void android_view_DisplayListCanvas_drawCircleProps(jlong canvasPtr,
|
||||
canvas->drawCircle(xProp, yProp, radiusProp, paintProp);
|
||||
}
|
||||
|
||||
static void android_view_DisplayListCanvas_drawWebViewFunctor(jlong canvasPtr, jint functor) {
|
||||
Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
|
||||
canvas->drawWebViewFunctor(functor);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// JNI Glue
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -192,6 +197,7 @@ static JNINativeMethod gMethods[] = {
|
||||
{ "nDrawTextureLayer", "(JJ)V", (void*) android_view_DisplayListCanvas_drawTextureLayer },
|
||||
{ "nDrawCircle", "(JJJJJ)V", (void*) android_view_DisplayListCanvas_drawCircleProps },
|
||||
{ "nDrawRoundRect", "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
|
||||
{ "nDrawWebViewFunctor", "(JI)V", (void*) android_view_DisplayListCanvas_drawWebViewFunctor },
|
||||
};
|
||||
|
||||
int register_android_view_DisplayListCanvas(JNIEnv* env) {
|
||||
|
||||
@@ -189,6 +189,14 @@ public final class RecordingCanvas extends DisplayListCanvas {
|
||||
nCallDrawGLFunction(mNativeCanvasWrapper, drawGLFunctor, releasedCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the provided functor that was created via WebViewFunctor_create()
|
||||
* @hide
|
||||
*/
|
||||
public void drawWebViewFunctor(int functor) {
|
||||
nDrawWebViewFunctor(mNativeCanvasWrapper, functor);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Display list
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@@ -303,4 +311,6 @@ public final class RecordingCanvas extends DisplayListCanvas {
|
||||
@CriticalNative
|
||||
private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
|
||||
long propRight, long propBottom, long propRx, long propRy, long propPaint);
|
||||
@CriticalNative
|
||||
private static native void nDrawWebViewFunctor(long canvas, int functor);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ cc_defaults {
|
||||
"hwui_lto",
|
||||
],
|
||||
|
||||
cpp_std: "experimental",
|
||||
|
||||
cflags: [
|
||||
"-DEGL_EGLEXT_PROTOTYPES",
|
||||
"-DGL_GLEXT_PROTOTYPES",
|
||||
@@ -224,6 +226,7 @@ cc_defaults {
|
||||
"RenderProperties.cpp",
|
||||
"SkiaCanvas.cpp",
|
||||
"TreeInfo.cpp",
|
||||
"WebViewFunctorManager.cpp",
|
||||
"VectorDrawable.cpp",
|
||||
"protos/graphicsstats.proto",
|
||||
],
|
||||
@@ -328,6 +331,7 @@ cc_test {
|
||||
"tests/unit/TypefaceTests.cpp",
|
||||
"tests/unit/VectorDrawableTests.cpp",
|
||||
"tests/unit/VectorDrawableAtlasTests.cpp",
|
||||
"tests/unit/WebViewFunctorManagerTests.cpp",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@@ -288,7 +288,10 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) {
|
||||
mDisplayList = mStagingDisplayList;
|
||||
mStagingDisplayList = nullptr;
|
||||
if (mDisplayList) {
|
||||
mDisplayList->syncContents();
|
||||
WebViewSyncData syncData {
|
||||
.applyForceDark = info && !info->disableForceDark
|
||||
};
|
||||
mDisplayList->syncContents(syncData);
|
||||
handleForceDark(info);
|
||||
}
|
||||
}
|
||||
|
||||
167
libs/hwui/WebViewFunctorManager.cpp
Normal file
167
libs/hwui/WebViewFunctorManager.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "WebViewFunctorManager.h"
|
||||
|
||||
#include <private/hwui/WebViewFunctor.h>
|
||||
#include "Properties.h"
|
||||
|
||||
#include <log/log.h>
|
||||
#include <utils/Trace.h>
|
||||
#include <atomic>
|
||||
|
||||
namespace android::uirenderer {
|
||||
|
||||
RenderMode WebViewFunctor_queryPlatformRenderMode() {
|
||||
auto pipelineType = Properties::getRenderPipelineType();
|
||||
switch (pipelineType) {
|
||||
case RenderPipelineType::SkiaGL:
|
||||
return RenderMode::OpenGL_ES;
|
||||
case RenderPipelineType::SkiaVulkan:
|
||||
return RenderMode::Vulkan;
|
||||
default:
|
||||
LOG_ALWAYS_FATAL("Unknown render pipeline type: %d", (int)pipelineType);
|
||||
}
|
||||
}
|
||||
|
||||
int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode) {
|
||||
if (functorMode != RenderMode::OpenGL_ES && functorMode != RenderMode::Vulkan) {
|
||||
ALOGW("Unknown rendermode %d", (int)functorMode);
|
||||
return -1;
|
||||
}
|
||||
if (functorMode == RenderMode::Vulkan &&
|
||||
WebViewFunctor_queryPlatformRenderMode() != RenderMode::Vulkan) {
|
||||
ALOGW("Unable to map from GLES platform to a vulkan functor");
|
||||
return -1;
|
||||
}
|
||||
return WebViewFunctorManager::instance().createFunctor(prototype, functorMode);
|
||||
}
|
||||
|
||||
void WebViewFunctor_release(int functor) {
|
||||
WebViewFunctorManager::instance().releaseFunctor(functor);
|
||||
}
|
||||
|
||||
static std::atomic_int sNextId{1};
|
||||
|
||||
WebViewFunctor::WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode) {
|
||||
mFunctor = sNextId++;
|
||||
mCallbacks = callbacks;
|
||||
mMode = functorMode;
|
||||
}
|
||||
|
||||
WebViewFunctor::~WebViewFunctor() {
|
||||
destroyContext();
|
||||
|
||||
ATRACE_NAME("WebViewFunctor::onDestroy");
|
||||
mCallbacks.onDestroyed(mFunctor);
|
||||
}
|
||||
|
||||
void WebViewFunctor::sync(const WebViewSyncData& syncData) const {
|
||||
ATRACE_NAME("WebViewFunctor::sync");
|
||||
mCallbacks.onSync(mFunctor, syncData);
|
||||
}
|
||||
|
||||
void WebViewFunctor::drawGl(const DrawGlInfo& drawInfo) {
|
||||
ATRACE_NAME("WebViewFunctor::drawGl");
|
||||
if (!mHasContext) {
|
||||
mHasContext = true;
|
||||
}
|
||||
mCallbacks.gles.draw(mFunctor, drawInfo);
|
||||
}
|
||||
|
||||
void WebViewFunctor::destroyContext() {
|
||||
if (mHasContext) {
|
||||
mHasContext = false;
|
||||
ATRACE_NAME("WebViewFunctor::onContextDestroyed");
|
||||
mCallbacks.onContextDestroyed(mFunctor);
|
||||
}
|
||||
}
|
||||
|
||||
WebViewFunctorManager& WebViewFunctorManager::instance() {
|
||||
static WebViewFunctorManager sInstance;
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
int WebViewFunctorManager::createFunctor(const WebViewFunctorCallbacks& callbacks,
|
||||
RenderMode functorMode) {
|
||||
auto object = std::make_unique<WebViewFunctor>(callbacks, functorMode);
|
||||
int id = object->id();
|
||||
auto handle = object->createHandle();
|
||||
{
|
||||
std::lock_guard _lock{mLock};
|
||||
mActiveFunctors.push_back(std::move(handle));
|
||||
mFunctors.push_back(std::move(object));
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
void WebViewFunctorManager::releaseFunctor(int functor) {
|
||||
sp<WebViewFunctor::Handle> toRelease;
|
||||
{
|
||||
std::lock_guard _lock{mLock};
|
||||
for (auto iter = mActiveFunctors.begin(); iter != mActiveFunctors.end(); iter++) {
|
||||
if ((*iter)->id() == functor) {
|
||||
toRelease = std::move(*iter);
|
||||
mActiveFunctors.erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WebViewFunctorManager::onContextDestroyed() {
|
||||
// WARNING: SKETCHY
|
||||
// Because we know that we always remove from mFunctors on RenderThread, the same
|
||||
// thread that always invokes onContextDestroyed, we know that the functor pointers
|
||||
// will remain valid without the lock held.
|
||||
// However, we won't block new functors from being added in the meantime.
|
||||
mLock.lock();
|
||||
const size_t size = mFunctors.size();
|
||||
WebViewFunctor* toDestroyContext[size];
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
toDestroyContext[i] = mFunctors[i].get();
|
||||
}
|
||||
mLock.unlock();
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
toDestroyContext[i]->destroyContext();
|
||||
}
|
||||
}
|
||||
|
||||
void WebViewFunctorManager::destroyFunctor(int functor) {
|
||||
std::unique_ptr<WebViewFunctor> toRelease;
|
||||
{
|
||||
std::lock_guard _lock{mLock};
|
||||
for (auto iter = mFunctors.begin(); iter != mFunctors.end(); iter++) {
|
||||
if ((*iter)->id() == functor) {
|
||||
toRelease = std::move(*iter);
|
||||
mFunctors.erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sp<WebViewFunctor::Handle> WebViewFunctorManager::handleFor(int functor) {
|
||||
std::lock_guard _lock{mLock};
|
||||
for (auto& iter : mActiveFunctors) {
|
||||
if (iter->id() == functor) {
|
||||
return iter;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace android::uirenderer
|
||||
92
libs/hwui/WebViewFunctorManager.h
Normal file
92
libs/hwui/WebViewFunctorManager.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <private/hwui/WebViewFunctor.h>
|
||||
#include <renderthread/RenderProxy.h>
|
||||
|
||||
#include <utils/LightRefBase.h>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace android::uirenderer {
|
||||
|
||||
class WebViewFunctorManager;
|
||||
|
||||
class WebViewFunctor {
|
||||
public:
|
||||
WebViewFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
|
||||
~WebViewFunctor();
|
||||
|
||||
class Handle : public LightRefBase<Handle> {
|
||||
public:
|
||||
~Handle() { renderthread::RenderProxy::destroyFunctor(id()); }
|
||||
|
||||
int id() const { return mReference.id(); }
|
||||
|
||||
void sync(const WebViewSyncData& syncData) const { mReference.sync(syncData); }
|
||||
|
||||
void drawGl(const DrawGlInfo& drawInfo) const { mReference.drawGl(drawInfo); }
|
||||
|
||||
private:
|
||||
friend class WebViewFunctor;
|
||||
|
||||
Handle(WebViewFunctor& ref) : mReference(ref) {}
|
||||
|
||||
WebViewFunctor& mReference;
|
||||
};
|
||||
|
||||
int id() const { return mFunctor; }
|
||||
void sync(const WebViewSyncData& syncData) const;
|
||||
void drawGl(const DrawGlInfo& drawInfo);
|
||||
void destroyContext();
|
||||
|
||||
sp<Handle> createHandle() {
|
||||
LOG_ALWAYS_FATAL_IF(mCreatedHandle);
|
||||
mCreatedHandle = true;
|
||||
return sp<Handle>{new Handle(*this)};
|
||||
}
|
||||
|
||||
private:
|
||||
WebViewFunctorCallbacks mCallbacks;
|
||||
int mFunctor;
|
||||
RenderMode mMode;
|
||||
bool mHasContext = false;
|
||||
bool mCreatedHandle = false;
|
||||
};
|
||||
|
||||
class WebViewFunctorManager {
|
||||
public:
|
||||
static WebViewFunctorManager& instance();
|
||||
|
||||
int createFunctor(const WebViewFunctorCallbacks& callbacks, RenderMode functorMode);
|
||||
void releaseFunctor(int functor);
|
||||
void onContextDestroyed();
|
||||
void destroyFunctor(int functor);
|
||||
|
||||
sp<WebViewFunctor::Handle> handleFor(int functor);
|
||||
|
||||
private:
|
||||
WebViewFunctorManager() = default;
|
||||
~WebViewFunctorManager() = default;
|
||||
|
||||
std::mutex mLock;
|
||||
std::vector<std::unique_ptr<WebViewFunctor>> mFunctors;
|
||||
std::vector<sp<WebViewFunctor::Handle>> mActiveFunctors;
|
||||
};
|
||||
|
||||
} // namespace android::uirenderer
|
||||
@@ -178,6 +178,9 @@ public:
|
||||
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0;
|
||||
virtual void callDrawGLFunction(Functor* functor,
|
||||
uirenderer::GlFunctorLifecycleListener* listener) = 0;
|
||||
virtual void drawWebViewFunctor(int /*functor*/) {
|
||||
LOG_ALWAYS_FATAL("Not supported");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Canvas state operations
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
#include <SkCanvas.h>
|
||||
#include <SkDrawable.h>
|
||||
|
||||
#include <WebViewFunctorManager.h>
|
||||
#include <utils/Functor.h>
|
||||
#include <variant>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
@@ -35,17 +37,43 @@ namespace skiapipeline {
|
||||
class FunctorDrawable : public SkDrawable {
|
||||
public:
|
||||
FunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
|
||||
: mFunctor(functor), mListener(listener), mBounds(canvas->getLocalClipBounds()) {}
|
||||
: mBounds(canvas->getLocalClipBounds())
|
||||
, mAnyFunctor(std::in_place_type<LegacyFunctor>, functor, listener) {}
|
||||
|
||||
FunctorDrawable(int functor, SkCanvas* canvas)
|
||||
: mBounds(canvas->getLocalClipBounds())
|
||||
, mAnyFunctor(std::in_place_type<NewFunctor>, functor) {}
|
||||
|
||||
virtual ~FunctorDrawable() {}
|
||||
|
||||
virtual void syncFunctor() const = 0;
|
||||
virtual void syncFunctor(const WebViewSyncData& data) const {
|
||||
if (mAnyFunctor.index() == 0) {
|
||||
std::get<0>(mAnyFunctor).handle->sync(data);
|
||||
} else {
|
||||
(*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeSync, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual SkRect onGetBounds() override { return mBounds; }
|
||||
|
||||
Functor* mFunctor;
|
||||
sp<GlFunctorLifecycleListener> mListener;
|
||||
const SkRect mBounds;
|
||||
|
||||
struct LegacyFunctor {
|
||||
explicit LegacyFunctor(Functor* functor, GlFunctorLifecycleListener* listener)
|
||||
: functor(functor), listener(listener) {}
|
||||
Functor* functor;
|
||||
sp<GlFunctorLifecycleListener> listener;
|
||||
};
|
||||
|
||||
struct NewFunctor {
|
||||
explicit NewFunctor(int functor) {
|
||||
handle = WebViewFunctorManager::instance().handleFor(functor);
|
||||
}
|
||||
sp<WebViewFunctor::Handle> handle;
|
||||
};
|
||||
|
||||
std::variant<NewFunctor, LegacyFunctor> mAnyFunctor;
|
||||
};
|
||||
|
||||
} // namespace skiapipeline
|
||||
|
||||
@@ -17,29 +17,28 @@
|
||||
#include "GLFunctorDrawable.h"
|
||||
#include <GrContext.h>
|
||||
#include <private/hwui/DrawGlInfo.h>
|
||||
#include "FunctorDrawable.h"
|
||||
#include "GlFunctorLifecycleListener.h"
|
||||
#include "GrBackendSurface.h"
|
||||
#include "GrRenderTarget.h"
|
||||
#include "GrRenderTargetContext.h"
|
||||
#include "RenderNode.h"
|
||||
#include "SkAndroidFrameworkUtils.h"
|
||||
#include "SkClipStack.h"
|
||||
#include "SkRect.h"
|
||||
#include "GrBackendSurface.h"
|
||||
#include "GrRenderTarget.h"
|
||||
#include "GrRenderTargetContext.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
GLFunctorDrawable::~GLFunctorDrawable() {
|
||||
if (mListener.get() != nullptr) {
|
||||
mListener->onGlFunctorReleased(mFunctor);
|
||||
if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
|
||||
if (lp->listener) {
|
||||
lp->listener->onGlFunctorReleased(lp->functor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GLFunctorDrawable::syncFunctor() const {
|
||||
(*mFunctor)(DrawGlInfo::kModeSync, nullptr);
|
||||
}
|
||||
|
||||
static void setScissor(int viewportHeight, const SkIRect& clip) {
|
||||
SkASSERT(!clip.isEmpty());
|
||||
// transform to Y-flipped GL space, and prevent negatives
|
||||
@@ -49,14 +48,14 @@ static void setScissor(int viewportHeight, const SkIRect& clip) {
|
||||
}
|
||||
|
||||
static bool GetFboDetails(SkCanvas* canvas, GLuint* outFboID, SkISize* outFboSize) {
|
||||
GrRenderTargetContext *renderTargetContext =
|
||||
GrRenderTargetContext* renderTargetContext =
|
||||
canvas->internal_private_accessTopLayerRenderTargetContext();
|
||||
if (!renderTargetContext) {
|
||||
ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
|
||||
return false;
|
||||
}
|
||||
|
||||
GrRenderTarget *renderTarget = renderTargetContext->accessRenderTarget();
|
||||
GrRenderTarget* renderTarget = renderTargetContext->accessRenderTarget();
|
||||
if (!renderTarget) {
|
||||
ALOGW("Unable to extract renderTarget info from canvas; aborting GLFunctor draw");
|
||||
return false;
|
||||
@@ -94,16 +93,16 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
|
||||
sk_sp<SkSurface> tmpSurface;
|
||||
// we are in a state where there is an unclipped saveLayer
|
||||
if (fboID != 0 && !surfaceBounds.contains(clipBounds)) {
|
||||
|
||||
// create an offscreen layer and clear it
|
||||
SkImageInfo surfaceInfo = canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
|
||||
tmpSurface = SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes,
|
||||
surfaceInfo);
|
||||
SkImageInfo surfaceInfo =
|
||||
canvas->imageInfo().makeWH(clipBounds.width(), clipBounds.height());
|
||||
tmpSurface =
|
||||
SkSurface::MakeRenderTarget(canvas->getGrContext(), SkBudgeted::kYes, surfaceInfo);
|
||||
tmpSurface->getCanvas()->clear(SK_ColorTRANSPARENT);
|
||||
|
||||
GrGLFramebufferInfo fboInfo;
|
||||
if (!tmpSurface->getBackendRenderTarget(SkSurface::kFlushWrite_BackendHandleAccess)
|
||||
.getGLFramebufferInfo(&fboInfo)) {
|
||||
.getGLFramebufferInfo(&fboInfo)) {
|
||||
ALOGW("Unable to extract renderTarget info from offscreen canvas; aborting GLFunctor");
|
||||
return;
|
||||
}
|
||||
@@ -144,7 +143,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
|
||||
bool clearStencilAfterFunctor = false;
|
||||
if (CC_UNLIKELY(clipRegion.isComplex())) {
|
||||
// clear the stencil
|
||||
//TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil
|
||||
// TODO: move stencil clear and canvas flush to SkAndroidFrameworkUtils::clipWithStencil
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
glStencilMask(0x1);
|
||||
glClearStencil(0);
|
||||
@@ -163,7 +162,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
|
||||
|
||||
// GL ops get inserted here if previous flush is missing, which could dirty the stencil
|
||||
bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(tmpCanvas);
|
||||
tmpCanvas->flush(); //need this flush for the single op that draws into the stencil
|
||||
tmpCanvas->flush(); // need this flush for the single op that draws into the stencil
|
||||
|
||||
// ensure that the framebuffer that the webview will render into is bound before after we
|
||||
// draw into the stencil
|
||||
@@ -188,7 +187,11 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) {
|
||||
setScissor(info.height, clipRegion.getBounds());
|
||||
}
|
||||
|
||||
(*mFunctor)(DrawGlInfo::kModeDraw, &info);
|
||||
if (mAnyFunctor.index() == 0) {
|
||||
std::get<0>(mAnyFunctor).handle->drawGl(info);
|
||||
} else {
|
||||
(*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
|
||||
}
|
||||
|
||||
if (clearStencilAfterFunctor) {
|
||||
// clear stencil buffer as it may be used by Skia
|
||||
|
||||
@@ -31,11 +31,9 @@ namespace skiapipeline {
|
||||
*/
|
||||
class GLFunctorDrawable : public FunctorDrawable {
|
||||
public:
|
||||
GLFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener, SkCanvas* canvas)
|
||||
: FunctorDrawable(functor, listener, canvas) {}
|
||||
virtual ~GLFunctorDrawable();
|
||||
using FunctorDrawable::FunctorDrawable;
|
||||
|
||||
void syncFunctor() const override;
|
||||
virtual ~GLFunctorDrawable();
|
||||
|
||||
protected:
|
||||
void onDraw(SkCanvas* canvas) override;
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <utils/MathUtils.h>
|
||||
#include "LayerDrawable.h"
|
||||
#include <utils/MathUtils.h>
|
||||
|
||||
#include "GrBackendSurface.h"
|
||||
#include "SkColorFilter.h"
|
||||
@@ -44,10 +44,9 @@ static bool shouldFilter(const SkMatrix& matrix) {
|
||||
if (!matrix.isScaleTranslate()) return true;
|
||||
|
||||
// We only care about meaningful scale here
|
||||
bool noScale = MathUtils::isOne(matrix.getScaleX())
|
||||
&& MathUtils::isOne(matrix.getScaleY());
|
||||
bool pixelAligned = SkScalarIsInt(matrix.getTranslateX())
|
||||
&& SkScalarIsInt(matrix.getTranslateY());
|
||||
bool noScale = MathUtils::isOne(matrix.getScaleX()) && MathUtils::isOne(matrix.getScaleY());
|
||||
bool pixelAligned =
|
||||
SkScalarIsInt(matrix.getTranslateX()) && SkScalarIsInt(matrix.getTranslateY());
|
||||
return !(noScale && pixelAligned);
|
||||
}
|
||||
|
||||
@@ -120,11 +119,12 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer
|
||||
// Integer translation is defined as when src rect and dst rect align fractionally.
|
||||
// Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
|
||||
// only for SrcOver blending and without color filter (readback uses Src blending).
|
||||
bool isIntegerTranslate = isBasicallyTranslate(totalMatrix)
|
||||
&& SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX])
|
||||
== SkScalarFraction(skiaSrcRect.fLeft)
|
||||
&& SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY])
|
||||
== SkScalarFraction(skiaSrcRect.fTop);
|
||||
bool isIntegerTranslate =
|
||||
isBasicallyTranslate(totalMatrix) &&
|
||||
SkScalarFraction(skiaDestRect.fLeft + totalMatrix[SkMatrix::kMTransX]) ==
|
||||
SkScalarFraction(skiaSrcRect.fLeft) &&
|
||||
SkScalarFraction(skiaDestRect.fTop + totalMatrix[SkMatrix::kMTransY]) ==
|
||||
SkScalarFraction(skiaSrcRect.fTop);
|
||||
if (layer->getForceFilter() || !isIntegerTranslate) {
|
||||
paint.setFilterQuality(kLow_SkFilterQuality);
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ class LayerDrawable : public SkDrawable {
|
||||
public:
|
||||
explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
|
||||
|
||||
static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
|
||||
const SkRect* srcRect, const SkRect* dstRect, bool useLayerTransform);
|
||||
static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer, const SkRect* srcRect,
|
||||
const SkRect* dstRect, bool useLayerTransform);
|
||||
|
||||
protected:
|
||||
virtual SkRect onGetBounds() override {
|
||||
|
||||
@@ -127,6 +127,7 @@ public:
|
||||
mNode.markDrawEnd(mCanvas);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SkCanvas& mCanvas;
|
||||
RenderNode& mNode;
|
||||
@@ -140,7 +141,7 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) {
|
||||
// ensures that we paint the layer even if it is not currently visible in the
|
||||
// event that the properties change and it becomes visible.
|
||||
if ((mProjectedDisplayList == nullptr && !renderNode->isRenderable()) ||
|
||||
(renderNode->nothingToDraw() && mComposeLayer)) {
|
||||
(renderNode->nothingToDraw() && mComposeLayer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -234,8 +235,8 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
|
||||
// we need to restrict the portion of the surface drawn to the size of the renderNode.
|
||||
SkASSERT(renderNode->getLayerSurface()->width() >= bounds.width());
|
||||
SkASSERT(renderNode->getLayerSurface()->height() >= bounds.height());
|
||||
canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(),
|
||||
bounds, bounds, &paint);
|
||||
canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot().get(), bounds,
|
||||
bounds, &paint);
|
||||
|
||||
if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
|
||||
renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
*/
|
||||
|
||||
#include "ShaderCache.h"
|
||||
#include <algorithm>
|
||||
#include <log/log.h>
|
||||
#include <thread>
|
||||
#include <array>
|
||||
#include <openssl/sha.h>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
#include "FileBlobCache.h"
|
||||
#include "Properties.h"
|
||||
#include "utils/TraceUtils.h"
|
||||
@@ -44,8 +44,7 @@ ShaderCache& ShaderCache::get() {
|
||||
}
|
||||
|
||||
bool ShaderCache::validateCache(const void* identity, ssize_t size) {
|
||||
if (nullptr == identity && size == 0)
|
||||
return true;
|
||||
if (nullptr == identity && size == 0) return true;
|
||||
|
||||
if (nullptr == identity || size < 0) {
|
||||
if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
|
||||
@@ -66,8 +65,7 @@ bool ShaderCache::validateCache(const void* identity, ssize_t size) {
|
||||
auto key = sIDKey;
|
||||
auto loaded = mBlobCache->get(&key, sizeof(key), hash.data(), hash.size());
|
||||
|
||||
if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin()))
|
||||
return true;
|
||||
if (loaded && std::equal(hash.begin(), hash.end(), mIDHash.begin())) return true;
|
||||
|
||||
if (CC_UNLIKELY(Properties::debugLevel & kDebugCaches)) {
|
||||
ALOGW("ShaderCache::validateCache cache validation fails");
|
||||
@@ -119,7 +117,7 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) {
|
||||
int maxTries = 3;
|
||||
while (valueSize > mObservedBlobValueSize && maxTries > 0) {
|
||||
mObservedBlobValueSize = std::min(valueSize, maxValueSize);
|
||||
void *newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
|
||||
void* newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
|
||||
if (!newValueBuffer) {
|
||||
free(valueBuffer);
|
||||
return nullptr;
|
||||
@@ -133,7 +131,7 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) {
|
||||
return nullptr;
|
||||
}
|
||||
if (valueSize > mObservedBlobValueSize) {
|
||||
ALOGE("ShaderCache::load value size is too big %d", (int) valueSize);
|
||||
ALOGE("ShaderCache::load value size is too big %d", (int)valueSize);
|
||||
free(valueBuffer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <GrContextOptions.h>
|
||||
#include <cutils/compiler.h>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <GrContextOptions.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
* the initialized state the load and store methods will return without
|
||||
* performing any cache operations.
|
||||
*/
|
||||
virtual void initShaderDiskCache(const void *identity, ssize_t size);
|
||||
virtual void initShaderDiskCache(const void* identity, ssize_t size);
|
||||
|
||||
virtual void initShaderDiskCache() { initShaderDiskCache(nullptr, 0); }
|
||||
|
||||
@@ -153,7 +153,7 @@ private:
|
||||
/**
|
||||
* "mObservedBlobValueSize" is the maximum value size observed by the cache reading function.
|
||||
*/
|
||||
size_t mObservedBlobValueSize = 20*1024;
|
||||
size_t mObservedBlobValueSize = 20 * 1024;
|
||||
|
||||
/**
|
||||
* The time in seconds to wait before saving newly inserted cache entries.
|
||||
@@ -176,7 +176,7 @@ private:
|
||||
*/
|
||||
static constexpr uint8_t sIDKey = 0;
|
||||
|
||||
friend class ShaderCacheTestUtils; //used for unit testing
|
||||
friend class ShaderCacheTestUtils; // used for unit testing
|
||||
};
|
||||
|
||||
} /* namespace skiapipeline */
|
||||
|
||||
@@ -27,9 +27,9 @@ namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
void SkiaDisplayList::syncContents() {
|
||||
void SkiaDisplayList::syncContents(const WebViewSyncData& data) {
|
||||
for (auto& functor : mChildFunctors) {
|
||||
functor->syncFunctor();
|
||||
functor->syncFunctor(data);
|
||||
}
|
||||
for (auto& animatedImage : mAnimatedImages) {
|
||||
animatedImage->syncProperties();
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hwui/AnimatedImageDrawable.h"
|
||||
#include "FunctorDrawable.h"
|
||||
#include "RecordingCanvas.h"
|
||||
#include "RenderNodeDrawable.h"
|
||||
#include "TreeInfo.h"
|
||||
#include "hwui/AnimatedImageDrawable.h"
|
||||
#include "utils/LinearAllocator.h"
|
||||
|
||||
#include <deque>
|
||||
@@ -109,7 +109,7 @@ public:
|
||||
* NOTE: This function can be folded into RenderNode when we no longer need
|
||||
* to subclass from DisplayList
|
||||
*/
|
||||
void syncContents();
|
||||
void syncContents(const WebViewSyncData& data);
|
||||
|
||||
/**
|
||||
* ONLY to be called by RenderNode::prepareTree in order to prepare this
|
||||
|
||||
@@ -21,16 +21,16 @@ namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
SkiaMemoryTracer::SkiaMemoryTracer(std::vector<ResourcePair> resourceMap, bool itemizeType)
|
||||
: mResourceMap(resourceMap)
|
||||
, mItemizeType(itemizeType)
|
||||
, mTotalSize("bytes", 0)
|
||||
, mPurgeableSize("bytes", 0) {}
|
||||
: mResourceMap(resourceMap)
|
||||
, mItemizeType(itemizeType)
|
||||
, mTotalSize("bytes", 0)
|
||||
, mPurgeableSize("bytes", 0) {}
|
||||
|
||||
SkiaMemoryTracer::SkiaMemoryTracer(const char* categoryKey, bool itemizeType)
|
||||
: mCategoryKey(categoryKey)
|
||||
, mItemizeType(itemizeType)
|
||||
, mTotalSize("bytes", 0)
|
||||
, mPurgeableSize("bytes", 0) {}
|
||||
: mCategoryKey(categoryKey)
|
||||
, mItemizeType(itemizeType)
|
||||
, mTotalSize("bytes", 0)
|
||||
, mPurgeableSize("bytes", 0) {}
|
||||
|
||||
const char* SkiaMemoryTracer::mapName(const char* resourceName) {
|
||||
for (auto& resource : mResourceMap) {
|
||||
@@ -42,7 +42,7 @@ const char* SkiaMemoryTracer::mapName(const char* resourceName) {
|
||||
}
|
||||
|
||||
void SkiaMemoryTracer::processElement() {
|
||||
if(!mCurrentElement.empty()) {
|
||||
if (!mCurrentElement.empty()) {
|
||||
// Only count elements that contain "size", other values just provide metadata.
|
||||
auto sizeResult = mCurrentValues.find("size");
|
||||
if (sizeResult != mCurrentValues.end()) {
|
||||
@@ -136,8 +136,8 @@ void SkiaMemoryTracer::logOutput(String8& log) {
|
||||
for (const auto& typedValue : namedItem.second) {
|
||||
TraceValue traceValue = convertUnits(typedValue.second);
|
||||
const char* entry = (traceValue.count > 1) ? "entries" : "entry";
|
||||
log.appendFormat(" %s: %.2f %s (%d %s)\n", typedValue.first,
|
||||
traceValue.value, traceValue.units, traceValue.count, entry);
|
||||
log.appendFormat(" %s: %.2f %s (%d %s)\n", typedValue.first, traceValue.value,
|
||||
traceValue.units, traceValue.count, entry);
|
||||
}
|
||||
} else {
|
||||
auto result = namedItem.second.find("size");
|
||||
|
||||
@@ -50,8 +50,8 @@ public:
|
||||
}
|
||||
|
||||
bool shouldDumpWrappedObjects() const override { return true; }
|
||||
void setMemoryBacking(const char*, const char*, const char*) override { }
|
||||
void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override { }
|
||||
void setMemoryBacking(const char*, const char*, const char*) override {}
|
||||
void setDiscardableMemoryBacking(const char*, const SkDiscardableMemory&) override {}
|
||||
|
||||
private:
|
||||
struct TraceValue {
|
||||
|
||||
@@ -183,15 +183,15 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator
|
||||
} else {
|
||||
String8 cachesOutput;
|
||||
mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
|
||||
&mRenderThread.renderState());
|
||||
&mRenderThread.renderState());
|
||||
ALOGE("%s", cachesOutput.string());
|
||||
if (errorHandler) {
|
||||
std::ostringstream err;
|
||||
err << "Unable to create layer for " << node->getName();
|
||||
const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
|
||||
err << ", size " << info.width() << "x" << info.height() << " max size "
|
||||
<< maxTextureSize << " color type " << (int)info.colorType()
|
||||
<< " has context " << (int)(mRenderThread.getGrContext() != nullptr);
|
||||
<< maxTextureSize << " color type " << (int)info.colorType() << " has context "
|
||||
<< (int)(mRenderThread.getGrContext() != nullptr);
|
||||
errorHandler->onError(err.str());
|
||||
}
|
||||
}
|
||||
@@ -300,8 +300,7 @@ void SkiaPipeline::endCapture(SkSurface* surface) {
|
||||
mSavePictureProcessor->savePicture(data, mCapturedFile);
|
||||
} else {
|
||||
mSavePictureProcessor->savePicture(
|
||||
data,
|
||||
mCapturedFile + "_" + std::to_string(mCaptureSequence));
|
||||
data, mCapturedFile + "_" + std::to_string(mCaptureSequence));
|
||||
}
|
||||
mCaptureSequence--;
|
||||
}
|
||||
|
||||
@@ -97,8 +97,7 @@ public:
|
||||
return mLightCenter;
|
||||
}
|
||||
|
||||
static void updateLighting(const LightGeometry& lightGeometry,
|
||||
const LightInfo& lightInfo) {
|
||||
static void updateLighting(const LightGeometry& lightGeometry, const LightInfo& lightInfo) {
|
||||
mLightRadius = lightGeometry.radius;
|
||||
mAmbientShadowAlpha = lightInfo.ambientShadowAlpha;
|
||||
mSpotShadowAlpha = lightInfo.spotShadowAlpha;
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
#include "RenderNode.h"
|
||||
#include "pipeline/skia/AnimatedDrawables.h"
|
||||
#include "pipeline/skia/GLFunctorDrawable.h"
|
||||
#include "pipeline/skia/VkInteropFunctorDrawable.h"
|
||||
#include "pipeline/skia/VkFunctorDrawable.h"
|
||||
#include "pipeline/skia/VkInteropFunctorDrawable.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
@@ -95,8 +95,8 @@ void SkiaRecordingCanvas::insertReorderBarrier(bool enableReorder) {
|
||||
drawDrawable(drawable);
|
||||
}
|
||||
if (enableReorder) {
|
||||
mCurrentBarrier = mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(
|
||||
mDisplayList.get());
|
||||
mCurrentBarrier =
|
||||
mDisplayList->allocateDrawable<StartReorderBarrierDrawable>(mDisplayList.get());
|
||||
drawDrawable(mCurrentBarrier);
|
||||
}
|
||||
}
|
||||
@@ -127,11 +127,25 @@ void SkiaRecordingCanvas::callDrawGLFunction(Functor* functor,
|
||||
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
|
||||
// TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
|
||||
// interop is disabled/moved.
|
||||
functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor,
|
||||
listener, asSkCanvas());
|
||||
functorDrawable = mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(
|
||||
functor, listener, asSkCanvas());
|
||||
} else {
|
||||
functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener,
|
||||
asSkCanvas());
|
||||
functorDrawable =
|
||||
mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, listener, asSkCanvas());
|
||||
}
|
||||
mDisplayList->mChildFunctors.push_back(functorDrawable);
|
||||
drawDrawable(functorDrawable);
|
||||
}
|
||||
|
||||
void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
|
||||
FunctorDrawable* functorDrawable;
|
||||
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
|
||||
// TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
|
||||
// interop is disabled.
|
||||
functorDrawable =
|
||||
mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor, asSkCanvas());
|
||||
} else {
|
||||
functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas());
|
||||
}
|
||||
mDisplayList->mChildFunctors.push_back(functorDrawable);
|
||||
drawDrawable(functorDrawable);
|
||||
@@ -167,7 +181,7 @@ SkiaCanvas::PaintCoW&& SkiaRecordingCanvas::filterBitmap(PaintCoW&& paint,
|
||||
if (colorSpaceFilter) {
|
||||
if (tmpPaint.getColorFilter()) {
|
||||
tmpPaint.setColorFilter(SkColorFilter::MakeComposeFilter(
|
||||
tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
|
||||
tmpPaint.refColorFilter(), std::move(colorSpaceFilter)));
|
||||
} else {
|
||||
tmpPaint.setColorFilter(std::move(colorSpaceFilter));
|
||||
}
|
||||
@@ -248,8 +262,7 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch
|
||||
filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
|
||||
}
|
||||
sk_sp<SkImage> image = bitmap.makeImage();
|
||||
mRecorder.drawImageLattice(image, lattice, dst,
|
||||
filterPaint(std::move(filteredPaint)),
|
||||
mRecorder.drawImageLattice(image, lattice, dst, filterPaint(std::move(filteredPaint)),
|
||||
bitmap.palette());
|
||||
if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
|
||||
mDisplayList->mMutableImages.push_back(image.get());
|
||||
|
||||
@@ -74,6 +74,7 @@ public:
|
||||
virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
|
||||
virtual void callDrawGLFunction(Functor* functor,
|
||||
uirenderer::GlFunctorLifecycleListener* listener) override;
|
||||
void drawWebViewFunctor(int functor) override;
|
||||
|
||||
private:
|
||||
RecordingCanvas mRecorder;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace android {
|
||||
|
||||
static inline SkRect SkRectMakeLargest() {
|
||||
const SkScalar v = SK_ScalarMax;
|
||||
return { -v, -v, v, v };
|
||||
return {-v, -v, v, v};
|
||||
};
|
||||
|
||||
} /* namespace android */
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
#include "Readback.h"
|
||||
#include "SkiaPipeline.h"
|
||||
#include "SkiaProfileRenderer.h"
|
||||
#include "VkInteropFunctorDrawable.h"
|
||||
#include "renderstate/RenderState.h"
|
||||
#include "renderthread/Frame.h"
|
||||
#include "VkInteropFunctorDrawable.h"
|
||||
|
||||
#include <SkSurface.h>
|
||||
#include <SkTypes.h>
|
||||
|
||||
@@ -17,23 +17,21 @@
|
||||
#include "VkFunctorDrawable.h"
|
||||
#include <private/hwui/DrawVkInfo.h>
|
||||
|
||||
#include "thread/ThreadBase.h"
|
||||
#include "utils/TimeUtils.h"
|
||||
#include <GrBackendDrawableInfo.h>
|
||||
#include <thread>
|
||||
#include <SkImage.h>
|
||||
#include <utils/Color.h>
|
||||
#include <utils/Trace.h>
|
||||
#include <utils/TraceUtils.h>
|
||||
#include <SkImage.h>
|
||||
#include <vk/GrVkTypes.h>
|
||||
#include <thread>
|
||||
#include "thread/ThreadBase.h"
|
||||
#include "utils/TimeUtils.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
namespace skiapipeline {
|
||||
|
||||
VkFunctorDrawHandler::VkFunctorDrawHandler(Functor *functor)
|
||||
: INHERITED()
|
||||
, mFunctor(functor) {}
|
||||
VkFunctorDrawHandler::VkFunctorDrawHandler(Functor* functor) : INHERITED(), mFunctor(functor) {}
|
||||
|
||||
VkFunctorDrawHandler::~VkFunctorDrawHandler() {
|
||||
// TODO(cblume) Fill in the DrawVkInfo parameters.
|
||||
@@ -55,14 +53,12 @@ void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
|
||||
(*mFunctor)(DrawVkInfo::kModeComposite, &draw_vk_info);
|
||||
}
|
||||
|
||||
VkFunctorDrawable::VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
|
||||
SkCanvas* canvas)
|
||||
: FunctorDrawable(functor, listener, canvas) {}
|
||||
|
||||
VkFunctorDrawable::~VkFunctorDrawable() = default;
|
||||
|
||||
void VkFunctorDrawable::syncFunctor() const {
|
||||
(*mFunctor)(DrawVkInfo::kModeSync, nullptr);
|
||||
VkFunctorDrawable::~VkFunctorDrawable() {
|
||||
if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
|
||||
if (lp->listener) {
|
||||
lp->listener->onGlFunctorReleased(lp->functor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
|
||||
@@ -71,12 +67,17 @@ void VkFunctorDrawable::onDraw(SkCanvas* /*canvas*/) {
|
||||
}
|
||||
|
||||
std::unique_ptr<FunctorDrawable::GpuDrawHandler> VkFunctorDrawable::onSnapGpuDrawHandler(
|
||||
GrBackendApi backendApi, const SkMatrix& matrix) {
|
||||
GrBackendApi backendApi, const SkMatrix& matrix) {
|
||||
if (backendApi != GrBackendApi::kVulkan) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<VkFunctorDrawHandler> draw(new VkFunctorDrawHandler(mFunctor));
|
||||
return std::move(draw);
|
||||
std::unique_ptr<VkFunctorDrawHandler> draw;
|
||||
if (mAnyFunctor.index() == 0) {
|
||||
LOG_ALWAYS_FATAL("Not implemented");
|
||||
return nullptr;
|
||||
} else {
|
||||
return std::make_unique<VkFunctorDrawHandler>(std::get<1>(mAnyFunctor).functor);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace skiapipeline
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
|
||||
#include "FunctorDrawable.h"
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
#include <SkImageInfo.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
~VkFunctorDrawHandler() override;
|
||||
|
||||
void draw(const GrBackendDrawableInfo& info) override;
|
||||
|
||||
private:
|
||||
typedef GpuDrawHandler INHERITED;
|
||||
|
||||
@@ -48,17 +49,15 @@ private:
|
||||
*/
|
||||
class VkFunctorDrawable : public FunctorDrawable {
|
||||
public:
|
||||
VkFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
|
||||
SkCanvas* canvas);
|
||||
~VkFunctorDrawable() override;
|
||||
using FunctorDrawable::FunctorDrawable;
|
||||
|
||||
void syncFunctor() const override;
|
||||
~VkFunctorDrawable() override;
|
||||
|
||||
protected:
|
||||
// SkDrawable functions:
|
||||
void onDraw(SkCanvas* canvas) override;
|
||||
std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
|
||||
const SkMatrix& matrix) override;
|
||||
std::unique_ptr<FunctorDrawable::GpuDrawHandler> onSnapGpuDrawHandler(
|
||||
GrBackendApi backendApi, const SkMatrix& matrix) override;
|
||||
};
|
||||
|
||||
} // namespace skiapipeline
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
#include "VkInteropFunctorDrawable.h"
|
||||
#include <private/hwui/DrawGlInfo.h>
|
||||
|
||||
#include "renderthread/EglManager.h"
|
||||
#include "thread/ThreadBase.h"
|
||||
#include "utils/TimeUtils.h"
|
||||
#include <thread>
|
||||
#include <utils/Color.h>
|
||||
#include <utils/Trace.h>
|
||||
#include <utils/TraceUtils.h>
|
||||
#include <thread>
|
||||
#include "renderthread/EglManager.h"
|
||||
#include "thread/ThreadBase.h"
|
||||
#include "utils/TimeUtils.h"
|
||||
|
||||
#include <EGL/eglext.h>
|
||||
#include <GLES2/gl2.h>
|
||||
@@ -44,6 +44,7 @@ static renderthread::EglManager sEglManager;
|
||||
class ScopedDrawRequest {
|
||||
public:
|
||||
ScopedDrawRequest() { beginDraw(); }
|
||||
|
||||
private:
|
||||
void beginDraw() {
|
||||
std::lock_guard _lock{sLock};
|
||||
@@ -57,9 +58,7 @@ private:
|
||||
}
|
||||
|
||||
if (!sEglManager.hasEglContext()) {
|
||||
sGLDrawThread->queue().runSync([]() {
|
||||
sEglManager.initialize();
|
||||
});
|
||||
sGLDrawThread->queue().runSync([]() { sEglManager.initialize(); });
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -93,14 +92,14 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
|
||||
if (!mFrameBuffer.get() || mFBInfo != surfaceInfo) {
|
||||
// Buffer will be used as an OpenGL ES render target.
|
||||
mFrameBuffer = new GraphicBuffer(
|
||||
//TODO: try to reduce the size of the buffer: possibly by using clip bounds.
|
||||
static_cast<uint32_t>(surfaceInfo.width()),
|
||||
static_cast<uint32_t>(surfaceInfo.height()),
|
||||
ColorTypeToPixelFormat(surfaceInfo.colorType()),
|
||||
GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
|
||||
GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
|
||||
std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
|
||||
"]");
|
||||
// TODO: try to reduce the size of the buffer: possibly by using clip bounds.
|
||||
static_cast<uint32_t>(surfaceInfo.width()),
|
||||
static_cast<uint32_t>(surfaceInfo.height()),
|
||||
ColorTypeToPixelFormat(surfaceInfo.colorType()),
|
||||
GraphicBuffer::USAGE_HW_TEXTURE | GraphicBuffer::USAGE_SW_WRITE_NEVER |
|
||||
GraphicBuffer::USAGE_SW_READ_NEVER | GraphicBuffer::USAGE_HW_RENDER,
|
||||
std::string("VkInteropFunctorDrawable::onDraw pid [") + std::to_string(getpid()) +
|
||||
"]");
|
||||
status_t error = mFrameBuffer->initCheck();
|
||||
if (error < 0) {
|
||||
ALOGW("VkInteropFunctorDrawable::onDraw() failed in GraphicBuffer.create()");
|
||||
@@ -110,16 +109,15 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
|
||||
mFBInfo = surfaceInfo;
|
||||
}
|
||||
|
||||
//TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
|
||||
//TODO: draw command has completed.
|
||||
//TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
|
||||
//TODO: GrVkGpu::destroyResources() for example.
|
||||
// TODO: Synchronization is needed on mFrameBuffer to guarantee that the previous Vulkan
|
||||
// TODO: draw command has completed.
|
||||
// TODO: A simple but inefficient way is to flush and issue a QueueWaitIdle call. See
|
||||
// TODO: GrVkGpu::destroyResources() for example.
|
||||
bool success = sGLDrawThread->queue().runSync([&]() -> bool {
|
||||
ATRACE_FORMAT("WebViewDraw_%dx%d", mFBInfo.width(), mFBInfo.height());
|
||||
EGLDisplay display = sEglManager.eglDisplay();
|
||||
LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY,
|
||||
"Failed to get EGL_DEFAULT_DISPLAY! err=%s",
|
||||
uirenderer::renderthread::EglManager::eglErrorString());
|
||||
LOG_ALWAYS_FATAL_IF(display == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s",
|
||||
uirenderer::renderthread::EglManager::eglErrorString());
|
||||
// We use an EGLImage to access the content of the GraphicBuffer
|
||||
// The EGL image is later bound to a 2D texture
|
||||
EGLClientBuffer clientBuffer = (EGLClientBuffer)mFrameBuffer->getNativeBuffer();
|
||||
@@ -154,10 +152,10 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
|
||||
AutoGLFramebuffer glFb;
|
||||
// Bind texture to the frame buffer.
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
|
||||
glTexture.mTexture, 0);
|
||||
glTexture.mTexture, 0);
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
ALOGE("Failed framebuffer check for created target buffer: %s",
|
||||
GLUtils::getGLFramebufferError());
|
||||
GLUtils::getGLFramebufferError());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -166,19 +164,22 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
(*mFunctor)(DrawGlInfo::kModeDraw, &info);
|
||||
if (mAnyFunctor.index() == 0) {
|
||||
std::get<0>(mAnyFunctor).handle->drawGl(info);
|
||||
} else {
|
||||
(*(std::get<1>(mAnyFunctor).functor))(DrawGlInfo::kModeDraw, &info);
|
||||
}
|
||||
|
||||
EGLSyncKHR glDrawFinishedFence =
|
||||
eglCreateSyncKHR(eglGetCurrentDisplay(), EGL_SYNC_FENCE_KHR, NULL);
|
||||
LOG_ALWAYS_FATAL_IF(glDrawFinishedFence == EGL_NO_SYNC_KHR,
|
||||
"Could not create sync fence %#x", eglGetError());
|
||||
"Could not create sync fence %#x", eglGetError());
|
||||
glFlush();
|
||||
// TODO: export EGLSyncKHR in file descr
|
||||
// TODO: import file desc in Vulkan Semaphore
|
||||
// TODO: instead block the GPU: probably by using external Vulkan semaphore.
|
||||
// Block the CPU until the glFlush finish.
|
||||
EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0,
|
||||
FENCE_TIMEOUT);
|
||||
EGLint waitStatus = eglClientWaitSyncKHR(display, glDrawFinishedFence, 0, FENCE_TIMEOUT);
|
||||
LOG_ALWAYS_FATAL_IF(waitStatus != EGL_CONDITION_SATISFIED_KHR,
|
||||
"Failed to wait for the fence %#x", eglGetError());
|
||||
eglDestroySyncKHR(display, glDrawFinishedFence);
|
||||
@@ -197,26 +198,25 @@ void VkInteropFunctorDrawable::onDraw(SkCanvas* canvas) {
|
||||
canvas->resetMatrix();
|
||||
|
||||
auto functorImage = SkImage::MakeFromAHardwareBuffer(
|
||||
reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType,
|
||||
nullptr, kBottomLeft_GrSurfaceOrigin);
|
||||
reinterpret_cast<AHardwareBuffer*>(mFrameBuffer.get()), kPremul_SkAlphaType, nullptr,
|
||||
kBottomLeft_GrSurfaceOrigin);
|
||||
canvas->drawImage(functorImage, 0, 0, &paint);
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
VkInteropFunctorDrawable::~VkInteropFunctorDrawable() {
|
||||
if (mListener.get() != nullptr) {
|
||||
ScopedDrawRequest _drawRequest{};
|
||||
sGLDrawThread->queue().runSync([&]() {
|
||||
mListener->onGlFunctorReleased(mFunctor);
|
||||
});
|
||||
if (auto lp = std::get_if<LegacyFunctor>(&mAnyFunctor)) {
|
||||
if (lp->listener) {
|
||||
ScopedDrawRequest _drawRequest{};
|
||||
sGLDrawThread->queue().runSync(
|
||||
[&]() { lp->listener->onGlFunctorReleased(lp->functor); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VkInteropFunctorDrawable::syncFunctor() const {
|
||||
void VkInteropFunctorDrawable::syncFunctor(const WebViewSyncData& data) const {
|
||||
ScopedDrawRequest _drawRequest{};
|
||||
sGLDrawThread->queue().runSync([&]() {
|
||||
(*mFunctor)(DrawGlInfo::kModeSync, nullptr);
|
||||
});
|
||||
sGLDrawThread->queue().runSync([&]() { FunctorDrawable::syncFunctor(data); });
|
||||
}
|
||||
|
||||
} // namespace skiapipeline
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
#include "FunctorDrawable.h"
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
#include <ui/GraphicBuffer.h>
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
@@ -32,20 +32,18 @@ namespace skiapipeline {
|
||||
*/
|
||||
class VkInteropFunctorDrawable : public FunctorDrawable {
|
||||
public:
|
||||
VkInteropFunctorDrawable(Functor* functor, GlFunctorLifecycleListener* listener,
|
||||
SkCanvas* canvas)
|
||||
: FunctorDrawable(functor, listener, canvas) {}
|
||||
using FunctorDrawable::FunctorDrawable;
|
||||
|
||||
virtual ~VkInteropFunctorDrawable();
|
||||
|
||||
void syncFunctor() const override;
|
||||
|
||||
static void vkInvokeFunctor(Functor* functor);
|
||||
|
||||
void syncFunctor(const WebViewSyncData& data) const override;
|
||||
|
||||
protected:
|
||||
virtual void onDraw(SkCanvas* canvas) override;
|
||||
|
||||
private:
|
||||
|
||||
// Variables below describe/store temporary offscreen buffer used for Vulkan pipeline.
|
||||
sp<GraphicBuffer> mFrameBuffer;
|
||||
SkImageInfo mFBInfo;
|
||||
|
||||
82
libs/hwui/private/hwui/WebViewFunctor.h
Normal file
82
libs/hwui/private/hwui/WebViewFunctor.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
|
||||
#define FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
|
||||
|
||||
#include <private/hwui/DrawGlInfo.h>
|
||||
|
||||
namespace android::uirenderer {
|
||||
|
||||
enum class RenderMode {
|
||||
OpenGL_ES,
|
||||
Vulkan,
|
||||
};
|
||||
|
||||
// Static for the lifetime of the process
|
||||
RenderMode WebViewFunctor_queryPlatformRenderMode();
|
||||
|
||||
struct WebViewSyncData {
|
||||
bool applyForceDark;
|
||||
};
|
||||
|
||||
struct WebViewFunctorCallbacks {
|
||||
// kModeSync, called on RenderThread
|
||||
void (*onSync)(int functor, const WebViewSyncData& syncData);
|
||||
|
||||
// Called when either the context is destroyed _or_ when the functor's last reference goes
|
||||
// away. Will always be called with an active context and always on renderthread.
|
||||
void (*onContextDestroyed)(int functor);
|
||||
|
||||
// Called when the last reference to the handle goes away and the handle is considered
|
||||
// irrevocably destroyed. Will always be proceeded by a call to onContextDestroyed if
|
||||
// this functor had ever been drawn.
|
||||
void (*onDestroyed)(int functor);
|
||||
|
||||
union {
|
||||
struct {
|
||||
// Called on RenderThread. initialize is guaranteed to happen before this call
|
||||
void (*draw)(int functor, const DrawGlInfo& params);
|
||||
} gles;
|
||||
// TODO: VK support. The current DrawVkInfo is monolithic and needs to be split up for
|
||||
// what params are valid on what callbacks
|
||||
struct {
|
||||
// Called either the first time the functor is used or the first time it's used after
|
||||
// a call to onContextDestroyed.
|
||||
// void (*initialize)(int functor, const InitParams& params);
|
||||
// void (*frameStart)(int functor, /* todo: what params are actually needed for this to
|
||||
// be useful? Is this useful? */)
|
||||
// void (*draw)(int functor, const CompositeParams& params /* todo: rename - composite
|
||||
// almost always means something else, and we aren't compositing */);
|
||||
// void (*frameEnd)(int functor, const PostCompositeParams& params /* todo: same as
|
||||
// CompositeParams - rename */);
|
||||
} vk;
|
||||
};
|
||||
};
|
||||
|
||||
// Creates a new WebViewFunctor from the given prototype. The prototype is copied after
|
||||
// this function returns. Caller retains full ownership of it.
|
||||
// Returns -1 if the creation fails (such as an unsupported functorMode + platform mode combination)
|
||||
int WebViewFunctor_create(const WebViewFunctorCallbacks& prototype, RenderMode functorMode);
|
||||
|
||||
// May be called on any thread to signal that the functor should be destroyed.
|
||||
// The functor will receive an onDestroyed when the last usage of it is released,
|
||||
// and it should be considered alive & active until that point.
|
||||
void WebViewFunctor_release(int functor);
|
||||
|
||||
} // namespace android::uirenderer
|
||||
|
||||
#endif // FRAMEWORKS_BASE_WEBVIEWFUNCTOR_H
|
||||
@@ -576,8 +576,7 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) {
|
||||
ATRACE_CALL();
|
||||
if (level >= TRIM_MEMORY_COMPLETE) {
|
||||
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
|
||||
thread.destroyGlContext();
|
||||
thread.vulkanManager().destroy();
|
||||
thread.destroyRenderingContext();
|
||||
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
|
||||
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "renderthread/RenderThread.h"
|
||||
#include "utils/Macros.h"
|
||||
#include "utils/TimeUtils.h"
|
||||
#include "WebViewFunctorManager.h"
|
||||
|
||||
#include <ui/GraphicBuffer.h>
|
||||
|
||||
@@ -143,6 +144,14 @@ void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
|
||||
}
|
||||
}
|
||||
|
||||
void RenderProxy::destroyFunctor(int functor) {
|
||||
ATRACE_CALL();
|
||||
RenderThread& thread = RenderThread::getInstance();
|
||||
thread.queue().post([=]() {
|
||||
WebViewFunctorManager::instance().destroyFunctor(functor);
|
||||
});
|
||||
}
|
||||
|
||||
DeferredLayerUpdater* RenderProxy::createTextureLayer() {
|
||||
return mRenderThread.queue().runSync([this]() -> auto {
|
||||
return mContext->createTextureLayer();
|
||||
|
||||
@@ -82,6 +82,7 @@ public:
|
||||
ANDROID_API void destroy();
|
||||
|
||||
ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
|
||||
static void destroyFunctor(int functor);
|
||||
|
||||
ANDROID_API DeferredLayerUpdater* createTextureLayer();
|
||||
ANDROID_API void buildLayer(RenderNode* node);
|
||||
|
||||
@@ -132,6 +132,7 @@ RenderThread::RenderThread()
|
||||
, mFrameCallbackTaskPending(false)
|
||||
, mRenderState(nullptr)
|
||||
, mEglManager(nullptr)
|
||||
, mFunctorManager(WebViewFunctorManager::instance())
|
||||
, mVkManager(nullptr) {
|
||||
Properties::load();
|
||||
start("RenderThread");
|
||||
@@ -197,11 +198,13 @@ void RenderThread::requireGlContext() {
|
||||
setGrContext(grContext);
|
||||
}
|
||||
|
||||
void RenderThread::destroyGlContext() {
|
||||
void RenderThread::destroyRenderingContext() {
|
||||
mFunctorManager.onContextDestroyed();
|
||||
if (mEglManager->hasEglContext()) {
|
||||
setGrContext(nullptr);
|
||||
mEglManager->destroy();
|
||||
}
|
||||
vulkanManager().destroy();
|
||||
}
|
||||
|
||||
void RenderThread::dumpGraphicsMemory(int fd) {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "CacheManager.h"
|
||||
#include "TimeLord.h"
|
||||
#include "thread/ThreadBase.h"
|
||||
#include "WebViewFunctorManager.h"
|
||||
|
||||
#include <GrContext.h>
|
||||
#include <SkBitmap.h>
|
||||
@@ -104,7 +105,7 @@ public:
|
||||
void dumpGraphicsMemory(int fd);
|
||||
|
||||
void requireGlContext();
|
||||
void destroyGlContext();
|
||||
void destroyRenderingContext();
|
||||
|
||||
/**
|
||||
* isCurrent provides a way to query, if the caller is running on
|
||||
@@ -151,6 +152,7 @@ private:
|
||||
TimeLord mTimeLord;
|
||||
RenderState* mRenderState;
|
||||
EglManager* mEglManager;
|
||||
WebViewFunctorManager& mFunctorManager;
|
||||
|
||||
ProfileDataContainer mGlobalProfileData;
|
||||
Readback* mReadback = nullptr;
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
std::unordered_map<int, TestUtils::CallCounts> TestUtils::sMockFunctorCounts{};
|
||||
|
||||
SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) {
|
||||
int startA = (start >> 24) & 0xff;
|
||||
int startR = (start >> 16) & 0xff;
|
||||
@@ -82,12 +84,10 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint
|
||||
uint32_t length = strlen(text);
|
||||
SkPaint glyphPaint(paint);
|
||||
glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
|
||||
canvas->drawText(
|
||||
utf16.get(), length, // text buffer
|
||||
0, length, // draw range
|
||||
0, length, // context range
|
||||
x, y, minikin::Bidi::LTR,
|
||||
glyphPaint, nullptr, nullptr /* measured text */);
|
||||
canvas->drawText(utf16.get(), length, // text buffer
|
||||
0, length, // draw range
|
||||
0, length, // context range
|
||||
x, y, minikin::Bidi::LTR, glyphPaint, nullptr, nullptr /* measured text */);
|
||||
}
|
||||
|
||||
void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint,
|
||||
@@ -96,7 +96,7 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint
|
||||
SkPaint glyphPaint(paint);
|
||||
glyphPaint.setTextEncoding(kGlyphID_SkTextEncoding);
|
||||
canvas->drawTextOnPath(utf16.get(), strlen(text), minikin::Bidi::LTR, path, 0, 0, glyphPaint,
|
||||
nullptr);
|
||||
nullptr);
|
||||
}
|
||||
|
||||
void TestUtils::TestTask::run() {
|
||||
@@ -110,11 +110,7 @@ void TestUtils::TestTask::run() {
|
||||
|
||||
rtCallback(renderThread);
|
||||
|
||||
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
|
||||
renderThread.vulkanManager().destroy();
|
||||
} else {
|
||||
renderThread.destroyGlContext();
|
||||
}
|
||||
renderThread.destroyRenderingContext();
|
||||
}
|
||||
|
||||
std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) {
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <renderstate/RenderState.h>
|
||||
#include <renderthread/RenderThread.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <memory>
|
||||
|
||||
namespace android {
|
||||
@@ -201,8 +202,7 @@ public:
|
||||
|
||||
static void recordNode(RenderNode& node, std::function<void(Canvas&)> contentCallback) {
|
||||
std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas(
|
||||
node.stagingProperties().getWidth(), node.stagingProperties().getHeight(),
|
||||
&node));
|
||||
node.stagingProperties().getWidth(), node.stagingProperties().getHeight(), &node));
|
||||
contentCallback(*canvas.get());
|
||||
node.setStagingDisplayList(canvas->finishRecording());
|
||||
}
|
||||
@@ -267,7 +267,14 @@ public:
|
||||
renderthread::RenderThread::getInstance().queue().runSync([&]() { task.run(); });
|
||||
}
|
||||
|
||||
static void runOnRenderThreadUnmanaged(RtCallback rtCallback) {
|
||||
auto& rt = renderthread::RenderThread::getInstance();
|
||||
rt.queue().runSync([&]() { rtCallback(rt); });
|
||||
}
|
||||
|
||||
|
||||
static bool isRenderThreadRunning() { return renderthread::RenderThread::hasInstance(); }
|
||||
static pid_t getRenderThreadTid() { return renderthread::RenderThread::getInstance().getTid(); }
|
||||
|
||||
static SkColor interpolateColor(float fraction, SkColor start, SkColor end);
|
||||
|
||||
@@ -296,7 +303,52 @@ public:
|
||||
static SkRect getClipBounds(const SkCanvas* canvas);
|
||||
static SkRect getLocalClipBounds(const SkCanvas* canvas);
|
||||
|
||||
struct CallCounts {
|
||||
int sync = 0;
|
||||
int contextDestroyed = 0;
|
||||
int destroyed = 0;
|
||||
int glesDraw = 0;
|
||||
};
|
||||
|
||||
static void expectOnRenderThread() { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()); }
|
||||
|
||||
static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) {
|
||||
auto callbacks = WebViewFunctorCallbacks{
|
||||
.onSync =
|
||||
[](int functor, const WebViewSyncData& data) {
|
||||
expectOnRenderThread();
|
||||
sMockFunctorCounts[functor].sync++;
|
||||
},
|
||||
.onContextDestroyed =
|
||||
[](int functor) {
|
||||
expectOnRenderThread();
|
||||
sMockFunctorCounts[functor].contextDestroyed++;
|
||||
},
|
||||
.onDestroyed =
|
||||
[](int functor) {
|
||||
expectOnRenderThread();
|
||||
sMockFunctorCounts[functor].destroyed++;
|
||||
},
|
||||
};
|
||||
switch (mode) {
|
||||
case RenderMode::OpenGL_ES:
|
||||
callbacks.gles.draw = [](int functor, const DrawGlInfo& params) {
|
||||
expectOnRenderThread();
|
||||
sMockFunctorCounts[functor].glesDraw++;
|
||||
};
|
||||
break;
|
||||
default:
|
||||
ADD_FAILURE();
|
||||
return WebViewFunctorCallbacks{};
|
||||
}
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; }
|
||||
|
||||
private:
|
||||
static std::unordered_map<int, CallCounts> sMockFunctorCounts;
|
||||
|
||||
static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) {
|
||||
MarkAndSweepRemoved observer(nullptr);
|
||||
node->syncProperties();
|
||||
@@ -306,9 +358,9 @@ private:
|
||||
}
|
||||
auto displayList = node->getDisplayList();
|
||||
if (displayList) {
|
||||
for (auto&& childDr : static_cast<skiapipeline::SkiaDisplayList*>(
|
||||
const_cast<DisplayList*>(displayList))
|
||||
->mChildNodes) {
|
||||
for (auto&& childDr :
|
||||
static_cast<skiapipeline::SkiaDisplayList*>(const_cast<DisplayList*>(displayList))
|
||||
->mChildNodes) {
|
||||
syncHierarchyPropertiesAndDisplayListImpl(childDr.getRenderNode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
|
||||
#include "tests/common/TestUtils.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <SkBitmap.h>
|
||||
#include <SkImage.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace android;
|
||||
using namespace android::uirenderer;
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
// current thread can spoof being a GPU thread
|
||||
static void destroyEglContext() {
|
||||
if (TestUtils::isRenderThreadRunning()) {
|
||||
TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyGlContext(); });
|
||||
TestUtils::runOnRenderThread([](RenderThread& thread) { thread.destroyRenderingContext(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -355,9 +355,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
|
||||
class ProjectionTestCanvas : public SkCanvas {
|
||||
public:
|
||||
ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
|
||||
void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
|
||||
mDrawCounter++;
|
||||
}
|
||||
void onDrawRect(const SkRect& rect, const SkPaint& paint) override { mDrawCounter++; }
|
||||
|
||||
int getDrawCounter() { return mDrawCounter; }
|
||||
|
||||
@@ -370,7 +368,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
|
||||
[](RenderProperties& properties, SkiaRecordingCanvas& canvas) {
|
||||
properties.setProjectionReceiver(true);
|
||||
},
|
||||
"B"); // a receiver with an empty display list
|
||||
"B"); // a receiver with an empty display list
|
||||
|
||||
auto projectingRipple = TestUtils::createSkiaNode(
|
||||
0, 0, 100, 100,
|
||||
@@ -389,14 +387,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
|
||||
canvas.drawRenderNode(projectingRipple.get());
|
||||
},
|
||||
"C");
|
||||
auto parent = TestUtils::createSkiaNode(
|
||||
0, 0, 100, 100,
|
||||
[&receiverBackground, &child](RenderProperties& properties,
|
||||
SkiaRecordingCanvas& canvas) {
|
||||
canvas.drawRenderNode(receiverBackground.get());
|
||||
canvas.drawRenderNode(child.get());
|
||||
},
|
||||
"A");
|
||||
auto parent =
|
||||
TestUtils::createSkiaNode(0, 0, 100, 100,
|
||||
[&receiverBackground, &child](RenderProperties& properties,
|
||||
SkiaRecordingCanvas& canvas) {
|
||||
canvas.drawRenderNode(receiverBackground.get());
|
||||
canvas.drawRenderNode(child.get());
|
||||
},
|
||||
"A");
|
||||
ContextFactory contextFactory;
|
||||
std::unique_ptr<CanvasContext> canvasContext(
|
||||
CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
|
||||
@@ -1058,7 +1056,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
|
||||
public:
|
||||
FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
|
||||
void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
|
||||
const SkPaint* paint, SrcRectConstraint constraint) override {
|
||||
const SkPaint* paint, SrcRectConstraint constraint) override {
|
||||
mDrawCounter++;
|
||||
EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality());
|
||||
}
|
||||
@@ -1076,7 +1074,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
|
||||
FrameTestCanvas canvas;
|
||||
RenderNodeDrawable drawable(layerNode.get(), &canvas, true);
|
||||
canvas.drawDrawable(&drawable);
|
||||
EXPECT_EQ(1, canvas.mDrawCounter); //make sure the layer was composed
|
||||
EXPECT_EQ(1, canvas.mDrawCounter); // make sure the layer was composed
|
||||
|
||||
// clean up layer pointer, so we can safely destruct RenderNode
|
||||
layerNode->setLayerSurface(nullptr);
|
||||
@@ -1129,15 +1127,14 @@ TEST(ReorderBarrierDrawable, testShadowMatrix) {
|
||||
getTotalMatrix());
|
||||
} else {
|
||||
// Second invocation is preparing the matrix for an elevated RenderNodeDrawable.
|
||||
EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
|
||||
matrix);
|
||||
EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y),
|
||||
getTotalMatrix());
|
||||
EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), matrix);
|
||||
EXPECT_EQ(SkMatrix::MakeTrans(TRANSLATE_X, TRANSLATE_Y), getTotalMatrix());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
int mDrawCounter = 0;
|
||||
|
||||
private:
|
||||
bool mFirstDidConcat = true;
|
||||
};
|
||||
@@ -1174,14 +1171,14 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
|
||||
public:
|
||||
VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
|
||||
void onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst,
|
||||
const SkPaint* paint, SrcRectConstraint constraint) override {
|
||||
const SkPaint* paint, SrcRectConstraint constraint) override {
|
||||
const int index = mDrawCounter++;
|
||||
switch (index) {
|
||||
case 0:
|
||||
EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
|
||||
break;
|
||||
case 1:
|
||||
EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH/2, CANVAS_HEIGHT));
|
||||
EXPECT_EQ(dst, SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
|
||||
break;
|
||||
default:
|
||||
ADD_FAILURE();
|
||||
@@ -1191,17 +1188,18 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
|
||||
|
||||
VectorDrawable::Group* group = new VectorDrawable::Group();
|
||||
sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
|
||||
vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH/10, CANVAS_HEIGHT/10);
|
||||
vectorDrawable->mutateStagingProperties()->setScaledSize(CANVAS_WIDTH / 10, CANVAS_HEIGHT / 10);
|
||||
|
||||
auto node = TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
||||
[&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
|
||||
vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH,
|
||||
CANVAS_HEIGHT));
|
||||
canvas.drawVectorDrawable(vectorDrawable.get());
|
||||
vectorDrawable->mutateStagingProperties()->setBounds(SkRect::MakeWH(CANVAS_WIDTH/2,
|
||||
CANVAS_HEIGHT));
|
||||
canvas.drawVectorDrawable(vectorDrawable.get());
|
||||
});
|
||||
auto node =
|
||||
TestUtils::createSkiaNode(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
||||
[&](RenderProperties& props, SkiaRecordingCanvas& canvas) {
|
||||
vectorDrawable->mutateStagingProperties()->setBounds(
|
||||
SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT));
|
||||
canvas.drawVectorDrawable(vectorDrawable.get());
|
||||
vectorDrawable->mutateStagingProperties()->setBounds(
|
||||
SkRect::MakeWH(CANVAS_WIDTH / 2, CANVAS_HEIGHT));
|
||||
canvas.drawVectorDrawable(vectorDrawable.get());
|
||||
});
|
||||
|
||||
VectorDrawableTestCanvas canvas;
|
||||
RenderNodeDrawable drawable(node.get(), &canvas, true);
|
||||
|
||||
@@ -295,7 +295,8 @@ RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) {
|
||||
canvasContext->destroy();
|
||||
}
|
||||
|
||||
RENDERTHREAD_TEST(RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
|
||||
// TODO: Is this supposed to work in SkiaGL/SkiaVK?
|
||||
RENDERTHREAD_TEST(DISABLED_RenderNode, prepareTree_HwLayer_AVD_enqueueDamage) {
|
||||
VectorDrawable::Group* group = new VectorDrawable::Group();
|
||||
sp<VectorDrawableRoot> vectorDrawable(new VectorDrawableRoot(group));
|
||||
|
||||
|
||||
@@ -14,17 +14,17 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <dirent.h>
|
||||
#include <cutils/properties.h>
|
||||
#include <cstdint>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <utils/Log.h>
|
||||
#include "pipeline/skia/ShaderCache.h"
|
||||
#include <cstdint>
|
||||
#include "FileBlobCache.h"
|
||||
#include "pipeline/skia/ShaderCache.h"
|
||||
|
||||
using namespace android::uirenderer::skiapipeline;
|
||||
|
||||
@@ -66,7 +66,6 @@ public:
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
std::string getExternalStorageFolder() {
|
||||
@@ -82,14 +81,12 @@ bool folderExist(const std::string& folderName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool
|
||||
checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
|
||||
return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size()
|
||||
&& 0 == memcmp(shader1->data(), shader2->data(), shader1->size());
|
||||
inline bool checkShader(const sk_sp<SkData>& shader1, const sk_sp<SkData>& shader2) {
|
||||
return nullptr != shader1 && nullptr != shader2 && shader1->size() == shader2->size() &&
|
||||
0 == memcmp(shader1->data(), shader2->data(), shader1->size());
|
||||
}
|
||||
|
||||
inline bool
|
||||
checkShader(const sk_sp<SkData>& shader, const char* program) {
|
||||
inline bool checkShader(const sk_sp<SkData>& shader, const char* program) {
|
||||
sk_sp<SkData> shader2 = SkData::MakeWithCString(program);
|
||||
return checkShader(shader, shader2);
|
||||
}
|
||||
@@ -116,32 +113,31 @@ void genRandomData(std::vector<T>& buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define GrProgramDescTest(a) (*SkData::MakeWithCString(#a).get())
|
||||
|
||||
TEST(ShaderCacheTest, testWriteAndRead) {
|
||||
if (!folderExist(getExternalStorageFolder())) {
|
||||
//don't run the test if external storage folder is not available
|
||||
// don't run the test if external storage folder is not available
|
||||
return;
|
||||
}
|
||||
std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
|
||||
std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
|
||||
std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
|
||||
std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
|
||||
|
||||
//remove any test files from previous test run
|
||||
// remove any test files from previous test run
|
||||
int deleteFile = remove(cacheFile1.c_str());
|
||||
ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
|
||||
std::srand(0);
|
||||
|
||||
//read the cache from a file that does not exist
|
||||
// read the cache from a file that does not exist
|
||||
ShaderCache::get().setFilename(cacheFile1.c_str());
|
||||
ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
|
||||
ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); // disable deferred save
|
||||
ShaderCache::get().initShaderDiskCache();
|
||||
|
||||
//read a key - should not be found since the cache is empty
|
||||
// read a key - should not be found since the cache is empty
|
||||
sk_sp<SkData> outVS;
|
||||
ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
|
||||
|
||||
//write to the in-memory cache without storing on disk and verify we read the same values
|
||||
// write to the in-memory cache without storing on disk and verify we read the same values
|
||||
sk_sp<SkData> inVS;
|
||||
setShader(inVS, "sassas");
|
||||
ShaderCache::get().store(GrProgramDescTest(100), *inVS.get());
|
||||
@@ -152,23 +148,23 @@ TEST(ShaderCacheTest, testWriteAndRead) {
|
||||
ASSERT_NE((outVS = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
|
||||
ASSERT_TRUE(checkShader(outVS, "someVS"));
|
||||
|
||||
//store content to disk and release in-memory cache
|
||||
// store content to disk and release in-memory cache
|
||||
ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
|
||||
|
||||
//change to a file that does not exist and verify load fails
|
||||
// change to a file that does not exist and verify load fails
|
||||
ShaderCache::get().setFilename(cacheFile2.c_str());
|
||||
ShaderCache::get().initShaderDiskCache();
|
||||
ASSERT_EQ(ShaderCache::get().load(GrProgramDescTest(432)), sk_sp<SkData>());
|
||||
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
|
||||
|
||||
//load again content from disk from an existing file and check the data is read correctly
|
||||
// load again content from disk from an existing file and check the data is read correctly
|
||||
ShaderCache::get().setFilename(cacheFile1.c_str());
|
||||
ShaderCache::get().initShaderDiskCache();
|
||||
sk_sp<SkData> outVS2;
|
||||
ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
|
||||
ASSERT_TRUE(checkShader(outVS2, "someVS"));
|
||||
|
||||
//change data, store to disk, read back again and verify data has been changed
|
||||
// change data, store to disk, read back again and verify data has been changed
|
||||
setShader(inVS, "ewData1");
|
||||
ShaderCache::get().store(GrProgramDescTest(432), *inVS.get());
|
||||
ShaderCacheTestUtils::terminate(ShaderCache::get(), true);
|
||||
@@ -176,9 +172,8 @@ TEST(ShaderCacheTest, testWriteAndRead) {
|
||||
ASSERT_NE((outVS2 = ShaderCache::get().load(GrProgramDescTest(432))), sk_sp<SkData>());
|
||||
ASSERT_TRUE(checkShader(outVS2, "ewData1"));
|
||||
|
||||
|
||||
//write and read big data chunk (50K)
|
||||
size_t dataSize = 50*1024;
|
||||
// write and read big data chunk (50K)
|
||||
size_t dataSize = 50 * 1024;
|
||||
std::vector<uint8_t> dataBuffer(dataSize);
|
||||
genRandomData(dataBuffer);
|
||||
setShader(inVS, dataBuffer);
|
||||
@@ -194,31 +189,31 @@ TEST(ShaderCacheTest, testWriteAndRead) {
|
||||
|
||||
TEST(ShaderCacheTest, testCacheValidation) {
|
||||
if (!folderExist(getExternalStorageFolder())) {
|
||||
//don't run the test if external storage folder is not available
|
||||
// don't run the test if external storage folder is not available
|
||||
return;
|
||||
}
|
||||
std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
|
||||
std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
|
||||
std::string cacheFile1 = getExternalStorageFolder() + "/shaderCacheTest1";
|
||||
std::string cacheFile2 = getExternalStorageFolder() + "/shaderCacheTest2";
|
||||
|
||||
//remove any test files from previous test run
|
||||
// remove any test files from previous test run
|
||||
int deleteFile = remove(cacheFile1.c_str());
|
||||
ASSERT_TRUE(0 == deleteFile || ENOENT == errno);
|
||||
std::srand(0);
|
||||
|
||||
//generate identity and read the cache from a file that does not exist
|
||||
// generate identity and read the cache from a file that does not exist
|
||||
ShaderCache::get().setFilename(cacheFile1.c_str());
|
||||
ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); //disable deferred save
|
||||
ShaderCacheTestUtils::setSaveDelay(ShaderCache::get(), 0); // disable deferred save
|
||||
std::vector<uint8_t> identity(1024);
|
||||
genRandomData(identity);
|
||||
ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
|
||||
sizeof(decltype(identity)::value_type));
|
||||
ShaderCache::get().initShaderDiskCache(
|
||||
identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
|
||||
|
||||
// generate random content in cache and store to disk
|
||||
constexpr size_t numBlob(10);
|
||||
constexpr size_t keySize(1024);
|
||||
constexpr size_t dataSize(50 * 1024);
|
||||
|
||||
std::vector< std::pair<sk_sp<SkData>, sk_sp<SkData>> > blobVec(numBlob);
|
||||
std::vector<std::pair<sk_sp<SkData>, sk_sp<SkData>>> blobVec(numBlob);
|
||||
for (auto& blob : blobVec) {
|
||||
std::vector<uint8_t> keyBuffer(keySize);
|
||||
std::vector<uint8_t> dataBuffer(dataSize);
|
||||
@@ -237,47 +232,47 @@ TEST(ShaderCacheTest, testCacheValidation) {
|
||||
// change to a file that does not exist and verify validation fails
|
||||
ShaderCache::get().setFilename(cacheFile2.c_str());
|
||||
ShaderCache::get().initShaderDiskCache();
|
||||
ASSERT_FALSE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
|
||||
ASSERT_FALSE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
|
||||
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
|
||||
|
||||
// restore the original file and verify validation succeeds
|
||||
ShaderCache::get().setFilename(cacheFile1.c_str());
|
||||
ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
|
||||
sizeof(decltype(identity)::value_type));
|
||||
ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
|
||||
ShaderCache::get().initShaderDiskCache(
|
||||
identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
|
||||
ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
|
||||
for (const auto& blob : blobVec) {
|
||||
auto outVS = ShaderCache::get().load(*blob.first.get());
|
||||
ASSERT_TRUE( checkShader(outVS, blob.second) );
|
||||
ASSERT_TRUE(checkShader(outVS, blob.second));
|
||||
}
|
||||
|
||||
// generate error identity and verify load fails
|
||||
ShaderCache::get().initShaderDiskCache(identity.data(), -1);
|
||||
for (const auto& blob : blobVec) {
|
||||
ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
|
||||
ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
|
||||
}
|
||||
ShaderCache::get().initShaderDiskCache(nullptr, identity.size() *
|
||||
sizeof(decltype(identity)::value_type));
|
||||
ShaderCache::get().initShaderDiskCache(
|
||||
nullptr, identity.size() * sizeof(decltype(identity)::value_type));
|
||||
for (const auto& blob : blobVec) {
|
||||
ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
|
||||
ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
|
||||
}
|
||||
|
||||
// verify the cache validation again after load fails
|
||||
ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
|
||||
sizeof(decltype(identity)::value_type));
|
||||
ASSERT_TRUE( ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity) );
|
||||
ShaderCache::get().initShaderDiskCache(
|
||||
identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
|
||||
ASSERT_TRUE(ShaderCacheTestUtils::validateCache(ShaderCache::get(), identity));
|
||||
for (const auto& blob : blobVec) {
|
||||
auto outVS = ShaderCache::get().load(*blob.first.get());
|
||||
ASSERT_TRUE( checkShader(outVS, blob.second) );
|
||||
ASSERT_TRUE(checkShader(outVS, blob.second));
|
||||
}
|
||||
|
||||
// generate another identity and verify load fails
|
||||
for (auto& data : identity) {
|
||||
data += std::rand();
|
||||
}
|
||||
ShaderCache::get().initShaderDiskCache(identity.data(), identity.size() *
|
||||
sizeof(decltype(identity)::value_type));
|
||||
ShaderCache::get().initShaderDiskCache(
|
||||
identity.data(), identity.size() * sizeof(decltype(identity)::value_type));
|
||||
for (const auto& blob : blobVec) {
|
||||
ASSERT_EQ( ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>() );
|
||||
ASSERT_EQ(ShaderCache::get().load(*blob.first.get()), sk_sp<SkData>());
|
||||
}
|
||||
|
||||
ShaderCacheTestUtils::terminate(ShaderCache::get(), false);
|
||||
|
||||
@@ -100,16 +100,35 @@ TEST(SkiaDisplayList, syncContexts) {
|
||||
GLFunctorDrawable functorDrawable(&functor, nullptr, &dummyCanvas);
|
||||
skiaDL.mChildFunctors.push_back(&functorDrawable);
|
||||
|
||||
int functor2 = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
|
||||
RenderMode::OpenGL_ES);
|
||||
auto& counts = TestUtils::countsForFunctor(functor2);
|
||||
skiaDL.mChildFunctors.push_back(
|
||||
skiaDL.allocateDrawable<GLFunctorDrawable>(functor2, &dummyCanvas));
|
||||
WebViewFunctor_release(functor2);
|
||||
|
||||
SkRect bounds = SkRect::MakeWH(200, 200);
|
||||
VectorDrawableRoot vectorDrawable(new VectorDrawable::Group());
|
||||
vectorDrawable.mutateStagingProperties()->setBounds(bounds);
|
||||
skiaDL.mVectorDrawables.push_back(&vectorDrawable);
|
||||
|
||||
// ensure that the functor and vectorDrawable are properly synced
|
||||
skiaDL.syncContents();
|
||||
TestUtils::runOnRenderThread([&](auto&) {
|
||||
skiaDL.syncContents(WebViewSyncData{
|
||||
.applyForceDark = false,
|
||||
});
|
||||
});
|
||||
|
||||
ASSERT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
|
||||
ASSERT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
|
||||
EXPECT_EQ(functor.getLastMode(), DrawGlInfo::kModeSync);
|
||||
EXPECT_EQ(counts.sync, 1);
|
||||
EXPECT_EQ(counts.destroyed, 0);
|
||||
EXPECT_EQ(vectorDrawable.mutateProperties()->getBounds(), bounds);
|
||||
|
||||
skiaDL.reset();
|
||||
TestUtils::runOnRenderThread([](auto&) {
|
||||
// Fence
|
||||
});
|
||||
EXPECT_EQ(counts.destroyed, 1);
|
||||
}
|
||||
|
||||
class ContextFactory : public IContextFactory {
|
||||
|
||||
@@ -24,10 +24,10 @@
|
||||
#include "DamageAccumulator.h"
|
||||
#include "IContextFactory.h"
|
||||
#include "SkiaCanvas.h"
|
||||
#include "pipeline/skia/SkiaUtils.h"
|
||||
#include "pipeline/skia/SkiaDisplayList.h"
|
||||
#include "pipeline/skia/SkiaOpenGLPipeline.h"
|
||||
#include "pipeline/skia/SkiaRecordingCanvas.h"
|
||||
#include "pipeline/skia/SkiaUtils.h"
|
||||
#include "renderthread/CanvasContext.h"
|
||||
#include "tests/common/TestUtils.h"
|
||||
|
||||
@@ -51,8 +51,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
|
||||
auto surface = SkSurface::MakeRasterN32Premul(1, 1);
|
||||
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
|
||||
surface);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
|
||||
}
|
||||
|
||||
@@ -84,8 +83,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) {
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
|
||||
// drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
|
||||
surface);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
|
||||
}
|
||||
|
||||
@@ -106,12 +104,10 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
|
||||
auto surface = SkSurface::MakeRasterN32Premul(2, 2);
|
||||
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
|
||||
surface);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds,
|
||||
surface);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
|
||||
}
|
||||
@@ -130,8 +126,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
|
||||
auto surface = SkSurface::MakeRasterN32Premul(2, 2);
|
||||
surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds,
|
||||
surface);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorRED);
|
||||
@@ -203,38 +198,32 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE);
|
||||
|
||||
// Single draw, should be white.
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
|
||||
surface);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE);
|
||||
|
||||
// 1 Overdraw, should be blue blended onto white.
|
||||
renderNodes.push_back(whiteNode);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
|
||||
surface);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff);
|
||||
|
||||
// 2 Overdraw, should be green blended onto white
|
||||
renderNodes.push_back(whiteNode);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
|
||||
surface);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0);
|
||||
|
||||
// 3 Overdraw, should be pink blended onto white.
|
||||
renderNodes.push_back(whiteNode);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
|
||||
surface);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0);
|
||||
|
||||
// 4 Overdraw, should be red blended onto white.
|
||||
renderNodes.push_back(whiteNode);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
|
||||
surface);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
|
||||
|
||||
// 5 Overdraw, should be red blended onto white.
|
||||
renderNodes.push_back(whiteNode);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds,
|
||||
surface);
|
||||
pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface);
|
||||
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080);
|
||||
}
|
||||
|
||||
@@ -389,7 +378,6 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
|
||||
EXPECT_FALSE(pipeline->isSurfaceReady());
|
||||
EXPECT_TRUE(pipeline->setSurface((Surface*)0x01, SwapBehavior::kSwap_default, ColorMode::SRGB));
|
||||
EXPECT_TRUE(pipeline->isSurfaceReady());
|
||||
renderThread.destroyGlContext();
|
||||
renderThread.destroyRenderingContext();
|
||||
EXPECT_FALSE(pipeline->isSurfaceReady());
|
||||
}
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ std::shared_ptr<minikin::FontFamily> buildFamily(const char* fileName) {
|
||||
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
|
||||
sk_sp<SkTypeface> typeface(fm->makeFromStream(std::move(fontData)));
|
||||
LOG_ALWAYS_FATAL_IF(typeface == nullptr, "Failed to make typeface from %s", fileName);
|
||||
std::shared_ptr<minikin::MinikinFont> font = std::make_shared<MinikinFontSkia>(
|
||||
std::move(typeface), data, st.st_size, fileName, 0,
|
||||
std::vector<minikin::FontVariation>());
|
||||
std::shared_ptr<minikin::MinikinFont> font =
|
||||
std::make_shared<MinikinFontSkia>(std::move(typeface), data, st.st_size, fileName, 0,
|
||||
std::vector<minikin::FontVariation>());
|
||||
std::vector<minikin::Font> fonts;
|
||||
fonts.push_back(minikin::Font::Builder(font).build());
|
||||
return std::make_shared<minikin::FontFamily>(std::move(fonts));
|
||||
|
||||
@@ -85,8 +85,10 @@ const static TestData sTestDataSet[] = {
|
||||
outPath->rCubicTo(8.0, 8.0, 8.0, 8.0, 8.0, 8.0);
|
||||
outPath->cubicTo(16.0, 16.0, 9.0, 9.0, 9.0, 9.0);
|
||||
outPath->rCubicTo(0.0, 0.0, 9.0, 9.0, 9.0, 9.0);
|
||||
outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0, 10.0);
|
||||
outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0, 20.0);
|
||||
outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 10.0,
|
||||
10.0);
|
||||
outPath->arcTo(10.0, 10.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCW_Direction, 20.0,
|
||||
20.0);
|
||||
}},
|
||||
|
||||
// Check box VectorDrawable path data
|
||||
@@ -157,7 +159,8 @@ const static TestData sTestDataSet[] = {
|
||||
},
|
||||
[](SkPath* outPath) {
|
||||
outPath->moveTo(300.0, 70.0);
|
||||
outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction, 301.0, 70.0);
|
||||
outPath->arcTo(230.0, 230.0, 0.0, SkPath::kLarge_ArcSize, SkPath::kCCW_Direction,
|
||||
301.0, 70.0);
|
||||
outPath->close();
|
||||
outPath->moveTo(300.0, 70.0);
|
||||
}},
|
||||
@@ -236,14 +239,14 @@ struct StringPath {
|
||||
};
|
||||
|
||||
const StringPath sStringPaths[] = {
|
||||
{"3e...3", false}, // Not starting with a verb and ill-formatted float
|
||||
{"L.M.F.A.O", false}, // No floats following verbs
|
||||
{"m 1 1", true}, // Valid path data
|
||||
{"\n \t z", true}, // Valid path data with leading spaces
|
||||
{"1-2e34567", false}, // Not starting with a verb and ill-formatted float
|
||||
{"f 4 5", false}, // Invalid verb
|
||||
{"\r ", false}, // Empty string
|
||||
{"L1,0 L1,1 L0,1 z M1000", false} // Not enough floats following verb M.
|
||||
{"3e...3", false}, // Not starting with a verb and ill-formatted float
|
||||
{"L.M.F.A.O", false}, // No floats following verbs
|
||||
{"m 1 1", true}, // Valid path data
|
||||
{"\n \t z", true}, // Valid path data with leading spaces
|
||||
{"1-2e34567", false}, // Not starting with a verb and ill-formatted float
|
||||
{"f 4 5", false}, // Invalid verb
|
||||
{"\r ", false}, // Empty string
|
||||
{"L1,0 L1,1 L0,1 z M1000", false} // Not enough floats following verb M.
|
||||
};
|
||||
|
||||
static bool hasSameVerbs(const PathData& from, const PathData& to) {
|
||||
|
||||
154
libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
Normal file
154
libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "WebViewFunctorManager.h"
|
||||
#include "private/hwui/WebViewFunctor.h"
|
||||
#include "renderthread/RenderProxy.h"
|
||||
#include "tests/common/TestUtils.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace android;
|
||||
using namespace android::uirenderer;
|
||||
|
||||
TEST(WebViewFunctor, createDestroyGLES) {
|
||||
int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
|
||||
RenderMode::OpenGL_ES);
|
||||
ASSERT_NE(-1, functor);
|
||||
WebViewFunctor_release(functor);
|
||||
TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
|
||||
// Empty, don't care
|
||||
});
|
||||
auto& counts = TestUtils::countsForFunctor(functor);
|
||||
// We never initialized, so contextDestroyed == 0
|
||||
EXPECT_EQ(0, counts.contextDestroyed);
|
||||
EXPECT_EQ(1, counts.destroyed);
|
||||
}
|
||||
|
||||
TEST(WebViewFunctor, createSyncHandleGLES) {
|
||||
int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
|
||||
RenderMode::OpenGL_ES);
|
||||
ASSERT_NE(-1, functor);
|
||||
auto handle = WebViewFunctorManager::instance().handleFor(functor);
|
||||
ASSERT_TRUE(handle);
|
||||
WebViewFunctor_release(functor);
|
||||
EXPECT_FALSE(WebViewFunctorManager::instance().handleFor(functor));
|
||||
TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
|
||||
// fence
|
||||
});
|
||||
auto& counts = TestUtils::countsForFunctor(functor);
|
||||
EXPECT_EQ(0, counts.sync);
|
||||
EXPECT_EQ(0, counts.contextDestroyed);
|
||||
EXPECT_EQ(0, counts.destroyed);
|
||||
|
||||
TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
|
||||
WebViewSyncData syncData;
|
||||
handle->sync(syncData);
|
||||
});
|
||||
|
||||
EXPECT_EQ(1, counts.sync);
|
||||
|
||||
TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
|
||||
WebViewSyncData syncData;
|
||||
handle->sync(syncData);
|
||||
});
|
||||
|
||||
EXPECT_EQ(2, counts.sync);
|
||||
|
||||
handle.clear();
|
||||
|
||||
TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
|
||||
// fence
|
||||
});
|
||||
|
||||
EXPECT_EQ(2, counts.sync);
|
||||
EXPECT_EQ(0, counts.contextDestroyed);
|
||||
EXPECT_EQ(1, counts.destroyed);
|
||||
}
|
||||
|
||||
TEST(WebViewFunctor, createSyncDrawGLES) {
|
||||
int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
|
||||
RenderMode::OpenGL_ES);
|
||||
ASSERT_NE(-1, functor);
|
||||
auto handle = WebViewFunctorManager::instance().handleFor(functor);
|
||||
ASSERT_TRUE(handle);
|
||||
WebViewFunctor_release(functor);
|
||||
auto& counts = TestUtils::countsForFunctor(functor);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
|
||||
WebViewSyncData syncData;
|
||||
handle->sync(syncData);
|
||||
DrawGlInfo drawInfo;
|
||||
handle->drawGl(drawInfo);
|
||||
handle->drawGl(drawInfo);
|
||||
});
|
||||
}
|
||||
handle.clear();
|
||||
TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
|
||||
// fence
|
||||
});
|
||||
EXPECT_EQ(5, counts.sync);
|
||||
EXPECT_EQ(10, counts.glesDraw);
|
||||
EXPECT_EQ(1, counts.contextDestroyed);
|
||||
EXPECT_EQ(1, counts.destroyed);
|
||||
}
|
||||
|
||||
TEST(WebViewFunctor, contextDestroyed) {
|
||||
int functor = WebViewFunctor_create(TestUtils::createMockFunctor(RenderMode::OpenGL_ES),
|
||||
RenderMode::OpenGL_ES);
|
||||
ASSERT_NE(-1, functor);
|
||||
auto handle = WebViewFunctorManager::instance().handleFor(functor);
|
||||
ASSERT_TRUE(handle);
|
||||
WebViewFunctor_release(functor);
|
||||
auto& counts = TestUtils::countsForFunctor(functor);
|
||||
TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
|
||||
WebViewSyncData syncData;
|
||||
handle->sync(syncData);
|
||||
DrawGlInfo drawInfo;
|
||||
handle->drawGl(drawInfo);
|
||||
});
|
||||
EXPECT_EQ(1, counts.sync);
|
||||
EXPECT_EQ(1, counts.glesDraw);
|
||||
EXPECT_EQ(0, counts.contextDestroyed);
|
||||
EXPECT_EQ(0, counts.destroyed);
|
||||
TestUtils::runOnRenderThreadUnmanaged([](auto& rt) {
|
||||
rt.destroyRenderingContext();
|
||||
});
|
||||
EXPECT_EQ(1, counts.sync);
|
||||
EXPECT_EQ(1, counts.glesDraw);
|
||||
EXPECT_EQ(1, counts.contextDestroyed);
|
||||
EXPECT_EQ(0, counts.destroyed);
|
||||
TestUtils::runOnRenderThreadUnmanaged([&](auto&) {
|
||||
WebViewSyncData syncData;
|
||||
handle->sync(syncData);
|
||||
DrawGlInfo drawInfo;
|
||||
handle->drawGl(drawInfo);
|
||||
});
|
||||
EXPECT_EQ(2, counts.sync);
|
||||
EXPECT_EQ(2, counts.glesDraw);
|
||||
EXPECT_EQ(1, counts.contextDestroyed);
|
||||
EXPECT_EQ(0, counts.destroyed);
|
||||
handle.clear();
|
||||
TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) {
|
||||
// fence
|
||||
});
|
||||
EXPECT_EQ(2, counts.sync);
|
||||
EXPECT_EQ(2, counts.glesDraw);
|
||||
EXPECT_EQ(2, counts.contextDestroyed);
|
||||
EXPECT_EQ(1, counts.destroyed);
|
||||
}
|
||||
@@ -17,14 +17,14 @@
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "Properties.h"
|
||||
#include "debug/GlesDriver.h"
|
||||
#include "debug/NullGlesDriver.h"
|
||||
#include "hwui/Typeface.h"
|
||||
#include "Properties.h"
|
||||
#include "tests/common/LeakChecker.h"
|
||||
#include "thread/TaskProcessor.h"
|
||||
#include "thread/Task.h"
|
||||
#include "thread/TaskManager.h"
|
||||
#include "thread/TaskProcessor.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user