New WebViewFunctor API

Should function alongside existing functor API.

Bug: 120997728
Test: hwuiunit passes
Change-Id: I8f6143d0be1111431b55016f34de319f6b8c8910
This commit is contained in:
John Reck
2018-12-13 16:40:14 -08:00
parent 832c17330e
commit 283bb46846
48 changed files with 939 additions and 314 deletions

View File

@@ -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",
],
}

View File

@@ -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);
}
}

View 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

View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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();

View File

@@ -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

View File

@@ -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");

View File

@@ -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 {

View File

@@ -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--;
}

View File

@@ -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;

View File

@@ -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());

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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>

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View 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

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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());
}
}

View File

@@ -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;

View File

@@ -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(); });
}
}

View File

@@ -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);

View File

@@ -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));

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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());
}

View File

@@ -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));

View File

@@ -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) {

View 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);
}

View File

@@ -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>