Remove RenderPipelineType::OpenGL (1/many)

Just removes the define & all things referencing the define.

Test: hwui_unit passes
Change-Id: I3f98c271e23ef696c40addf260abdc0fb149a70d
This commit is contained in:
John Reck
2018-04-09 16:56:34 -07:00
parent 076b631dd9
commit 18f442eb2d
24 changed files with 20 additions and 5100 deletions

View File

@@ -345,33 +345,21 @@ cc_test {
srcs: [
"tests/unit/main.cpp",
"tests/unit/BakedOpDispatcherTests.cpp",
"tests/unit/BakedOpRendererTests.cpp",
"tests/unit/BakedOpStateTests.cpp",
"tests/unit/CacheManagerTests.cpp",
"tests/unit/CanvasContextTests.cpp",
"tests/unit/CanvasStateTests.cpp",
"tests/unit/ClipAreaTests.cpp",
"tests/unit/DamageAccumulatorTests.cpp",
"tests/unit/DeferredLayerUpdaterTests.cpp",
"tests/unit/DeviceInfoTests.cpp",
"tests/unit/FatVectorTests.cpp",
"tests/unit/FontRendererTests.cpp",
"tests/unit/FrameBuilderTests.cpp",
"tests/unit/GlopBuilderTests.cpp",
"tests/unit/GpuMemoryTrackerTests.cpp",
"tests/unit/GradientCacheTests.cpp",
"tests/unit/GraphicsStatsServiceTests.cpp",
"tests/unit/LayerUpdateQueueTests.cpp",
"tests/unit/LeakCheckTests.cpp",
"tests/unit/LinearAllocatorTests.cpp",
"tests/unit/MatrixTests.cpp",
"tests/unit/MeshStateTests.cpp",
"tests/unit/OffscreenBufferPoolTests.cpp",
"tests/unit/OpDumperTests.cpp",
"tests/unit/PathInterpolatorTests.cpp",
"tests/unit/RenderNodeDrawableTests.cpp",
"tests/unit/RecordingCanvasTests.cpp",
"tests/unit/RenderNodeTests.cpp",
"tests/unit/RenderPropertiesTests.cpp",
"tests/unit/ShaderCacheTests.cpp",
@@ -383,8 +371,6 @@ cc_test {
"tests/unit/SnapshotTests.cpp",
"tests/unit/StringUtilsTests.cpp",
"tests/unit/TestUtilsTests.cpp",
"tests/unit/TextDropShadowCacheTests.cpp",
"tests/unit/TextureCacheTests.cpp",
"tests/unit/ThreadBaseTests.cpp",
"tests/unit/TypefaceTests.cpp",
"tests/unit/VectorDrawableTests.cpp",

View File

@@ -193,15 +193,12 @@ RenderPipelineType Properties::getRenderPipelineType() {
}
char prop[PROPERTY_VALUE_MAX];
property_get(PROPERTY_RENDERER, prop, "skiagl");
if (!strcmp(prop, "skiagl")) {
ALOGD("Skia GL Pipeline");
sRenderPipelineType = RenderPipelineType::SkiaGL;
} else if (!strcmp(prop, "skiavk")) {
if (!strcmp(prop, "skiavk")) {
ALOGD("Skia Vulkan Pipeline");
sRenderPipelineType = RenderPipelineType::SkiaVulkan;
} else { //"opengl"
ALOGD("HWUI GL Pipeline");
sRenderPipelineType = RenderPipelineType::OpenGL;
} else { //"skiagl"
ALOGD("Skia GL Pipeline");
sRenderPipelineType = RenderPipelineType::SkiaGL;
}
return sRenderPipelineType;
}

View File

@@ -206,7 +206,7 @@ enum class OverdrawColorSet { Default = 0, Deuteranomaly };
enum class StencilClipDebug { Hide, ShowHighlight, ShowRegion };
enum class RenderPipelineType { OpenGL = 0, SkiaGL, SkiaVulkan, NotInitialized = 128 };
enum class RenderPipelineType { SkiaGL, SkiaVulkan, NotInitialized = 128 };
/**
* Renderthread-only singleton which manages several static rendering properties. Most of these

View File

@@ -69,9 +69,6 @@ CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::OpenGL:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
std::make_unique<OpenGLPipeline>(thread));
case RenderPipelineType::SkiaGL:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
@@ -86,28 +83,13 @@ CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
}
void CanvasContext::destroyLayer(RenderNode* node) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::OpenGL:
OpenGLPipeline::destroyLayer(node);
break;
case RenderPipelineType::SkiaGL:
case RenderPipelineType::SkiaVulkan:
skiapipeline::SkiaPipeline::destroyLayer(node);
break;
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
break;
}
skiapipeline::SkiaPipeline::destroyLayer(node);
}
void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor) {
ATRACE_CALL();
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::OpenGL:
OpenGLPipeline::invokeFunctor(thread, functor);
break;
case RenderPipelineType::SkiaGL:
skiapipeline::SkiaOpenGLPipeline::invokeFunctor(thread, functor);
break;
@@ -121,19 +103,7 @@ void CanvasContext::invokeFunctor(const RenderThread& thread, Functor* functor)
}
void CanvasContext::prepareToDraw(const RenderThread& thread, Bitmap* bitmap) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::OpenGL:
OpenGLPipeline::prepareToDraw(thread, bitmap);
break;
case RenderPipelineType::SkiaGL:
case RenderPipelineType::SkiaVulkan:
skiapipeline::SkiaPipeline::prepareToDraw(thread, bitmap);
break;
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
break;
}
skiapipeline::SkiaPipeline::prepareToDraw(thread, bitmap);
}
CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
@@ -586,37 +556,15 @@ void CanvasContext::destroyHardwareResources() {
}
void CanvasContext::trimMemory(RenderThread& thread, int level) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::OpenGL: {
// No context means nothing to free
if (!thread.eglManager().hasEglContext()) return;
ATRACE_CALL();
if (level >= TRIM_MEMORY_COMPLETE) {
thread.renderState().flush(Caches::FlushMode::Full);
thread.eglManager().destroy();
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
thread.renderState().flush(Caches::FlushMode::Moderate);
}
break;
}
case RenderPipelineType::SkiaGL:
case RenderPipelineType::SkiaVulkan: {
// No context means nothing to free
if (!thread.getGrContext()) return;
ATRACE_CALL();
if (level >= TRIM_MEMORY_COMPLETE) {
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
thread.eglManager().destroy();
thread.vulkanManager().destroy();
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
}
break;
}
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
break;
ATRACE_CALL();
if (!thread.getGrContext()) return;
ATRACE_CALL();
if (level >= TRIM_MEMORY_COMPLETE) {
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
thread.eglManager().destroy();
thread.vulkanManager().destroy();
} else if (level >= TRIM_MEMORY_UI_HIDDEN) {
thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
}
}

View File

@@ -175,16 +175,6 @@ void RenderThread::dumpGraphicsMemory(int fd) {
String8 pipeline;
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::OpenGL: {
if (Caches::hasInstance()) {
cachesOutput.appendFormat("Caches:\n");
Caches::getInstance().dumpMemoryUsage(cachesOutput);
} else {
cachesOutput.appendFormat("No caches instance.");
}
pipeline.appendFormat("FrameBuilder");
break;
}
case RenderPipelineType::SkiaGL: {
mCacheManager->dumpMemoryUsage(cachesOutput, mRenderState);
pipeline.appendFormat("Skia (OpenGL)");
@@ -208,9 +198,6 @@ Readback& RenderThread::readback() {
if (!mReadback) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::OpenGL:
mReadback = new OpenGLReadbackImpl(*this);
break;
case RenderPipelineType::SkiaGL:
mReadback = new skiapipeline::SkiaOpenGLReadback(*this);
break;
@@ -344,8 +331,6 @@ void RenderThread::pushBackFrameCallback(IFrameCallback* callback) {
sk_sp<Bitmap> RenderThread::allocateHardwareBitmap(SkBitmap& skBitmap) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::OpenGL:
return OpenGLPipeline::allocateHardwareBitmap(*this, skBitmap);
case RenderPipelineType::SkiaGL:
return skiapipeline::SkiaOpenGLPipeline::allocateHardwareBitmap(*this, skBitmap);
case RenderPipelineType::SkiaVulkan:

View File

@@ -53,9 +53,7 @@ SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end)
sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
renderthread::RenderThread& renderThread) {
android::uirenderer::renderthread::IRenderPipeline* pipeline;
if (Properties::getRenderPipelineType() == RenderPipelineType::OpenGL) {
pipeline = new renderthread::OpenGLPipeline(renderThread);
} else if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
pipeline = new skiapipeline::SkiaOpenGLPipeline(renderThread);
} else {
pipeline = new skiapipeline::SkiaVulkanPipeline(renderThread);

View File

@@ -60,18 +60,6 @@ namespace uirenderer {
Properties::overrideRenderPipelineType(oldType); \
};
/**
* Like gtests' TEST, but only runs with the OpenGL RenderPipelineType
*/
#define OPENGL_PIPELINE_TEST(test_case_name, test_name) \
class test_case_name##_##test_name##_HwuiTest { \
public: \
static void doTheThing(); \
}; \
INNER_PIPELINE_TEST(test_case_name, test_name, OpenGL, \
test_case_name##_##test_name##_HwuiTest::doTheThing()) \
void test_case_name##_##test_name##_HwuiTest::doTheThing()
#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \
TestUtils::runOnRenderThread( \
@@ -86,25 +74,12 @@ namespace uirenderer {
public: \
static void doTheThing(renderthread::RenderThread& renderThread); \
}; \
INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \
INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
/* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
/* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \
void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \
renderthread::RenderThread& renderThread)
/**
* Like RENDERTHREAD_TEST, but only runs with the OpenGL RenderPipelineType
*/
#define RENDERTHREAD_OPENGL_PIPELINE_TEST(test_case_name, test_name) \
class test_case_name##_##test_name##_RenderThreadTest { \
public: \
static void doTheThing(renderthread::RenderThread& renderThread); \
}; \
INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, OpenGL); \
void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \
renderthread::RenderThread& renderThread)
/**
* Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
*/

View File

@@ -67,7 +67,7 @@ OPTIONS:
--onscreen Render tests on device screen. By default tests
are offscreen rendered
--benchmark_format Set output format. Possible values are tabular, json, csv
--renderer=TYPE Sets the render pipeline to use. May be opengl, skiagl, or skiavk
--renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk
--render-ahead=NUM Sets how far to render-ahead. Must be 0 (default), 1, or 2.
)");
}
@@ -148,9 +148,7 @@ static bool setBenchmarkFormat(const char* format) {
}
static bool setRenderer(const char* renderer) {
if (!strcmp(renderer, "opengl")) {
Properties::overrideRenderPipelineType(RenderPipelineType::OpenGL);
} else if (!strcmp(renderer, "skiagl")) {
if (!strcmp(renderer, "skiagl")) {
Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
} else if (!strcmp(renderer, "skiavk")) {
Properties::overrideRenderPipelineType(RenderPipelineType::SkiaVulkan);

View File

@@ -1,289 +0,0 @@
/*
* Copyright (C) 2016 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 <BakedOpDispatcher.h>
#include <BakedOpRenderer.h>
#include <FrameBuilder.h>
#include <LayerUpdateQueue.h>
#include <RecordedOp.h>
#include <hwui/Paint.h>
#include <tests/common/TestUtils.h>
#include <utils/Color.h>
#include <SkBlurDrawLooper.h>
#include <SkDashPathEffect.h>
#include <SkPath.h>
using namespace android::uirenderer;
static BakedOpRenderer::LightInfo sLightInfo;
const FrameBuilder::LightGeometry sLightGeometry = {{100, 100, 100}, 50};
class ValidatingBakedOpRenderer : public BakedOpRenderer {
public:
ValidatingBakedOpRenderer(RenderState& renderState,
std::function<void(const Glop& glop)> validator)
: BakedOpRenderer(Caches::getInstance(), renderState, true, false, sLightInfo)
, mValidator(validator) {
mGlopReceiver = ValidatingGlopReceiver;
}
private:
static void ValidatingGlopReceiver(BakedOpRenderer& renderer, const Rect* dirtyBounds,
const ClipBase* clip, const Glop& glop) {
auto vbor = reinterpret_cast<ValidatingBakedOpRenderer*>(&renderer);
vbor->mValidator(glop);
}
std::function<void(const Glop& glop)> mValidator;
};
typedef void (*TestBakedOpReceiver)(BakedOpRenderer&, const BakedOpState&);
static void testUnmergedGlopDispatch(renderthread::RenderThread& renderThread, RecordedOp* op,
std::function<void(const Glop& glop)> glopVerifier,
int expectedGlopCount = 1) {
// Create op, and wrap with basic state.
LinearAllocator allocator;
auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 100));
auto state = BakedOpState::tryConstruct(allocator, *snapshot, *op);
ASSERT_NE(nullptr, state);
int glopCount = 0;
auto glopReceiver = [&glopVerifier, &glopCount, &expectedGlopCount](const Glop& glop) {
ASSERT_LE(glopCount++, expectedGlopCount) << expectedGlopCount << "glop(s) expected";
glopVerifier(glop);
};
ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver);
// Dispatch based on op type created, similar to Frame/LayerBuilder dispatch behavior
#define X(Type) \
[](BakedOpRenderer& renderer, const BakedOpState& state) { \
BakedOpDispatcher::on##Type(renderer, static_cast<const Type&>(*(state.op)), state); \
},
static TestBakedOpReceiver unmergedReceivers[] = BUILD_RENDERABLE_OP_LUT(X);
#undef X
unmergedReceivers[op->opId](renderer, *state);
ASSERT_EQ(expectedGlopCount, glopCount) << "Exactly " << expectedGlopCount
<< "Glop(s) expected";
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTexture_positionOvalArc) {
SkPaint strokePaint;
strokePaint.setStyle(SkPaint::kStroke_Style);
strokePaint.setStrokeWidth(4);
float intervals[] = {1.0f, 1.0f};
strokePaint.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
auto textureGlopVerifier = [](const Glop& glop) {
// validate glop produced by renderPathTexture (so texture, unit quad)
auto texture = glop.fill.texture.texture;
ASSERT_NE(nullptr, texture);
float expectedOffset = floor(4 * 1.5f + 0.5f);
EXPECT_EQ(expectedOffset, reinterpret_cast<PathTexture*>(texture)->offset)
<< "Should see conservative offset from PathCache::computeBounds";
Rect expectedBounds(10, 15, 20, 25);
expectedBounds.outset(expectedOffset);
Matrix4 expectedModelView;
expectedModelView.loadTranslate(10 - expectedOffset, 15 - expectedOffset, 0);
expectedModelView.scale(10 + 2 * expectedOffset, 10 + 2 * expectedOffset, 1);
EXPECT_EQ(expectedModelView, glop.transform.modelView)
<< "X and Y offsets, and scale both applied to model view";
};
// Arc and Oval will render functionally the same glop, differing only in texture content
ArcOp arcOp(Rect(10, 15, 20, 25), Matrix4::identity(), nullptr, &strokePaint, 0, 270, true);
testUnmergedGlopDispatch(renderThread, &arcOp, textureGlopVerifier);
OvalOp ovalOp(Rect(10, 15, 20, 25), Matrix4::identity(), nullptr, &strokePaint);
testUnmergedGlopDispatch(renderThread, &ovalOp, textureGlopVerifier);
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, onLayerOp_bufferless) {
SkPaint layerPaint;
layerPaint.setAlpha(128);
OffscreenBuffer* buffer = nullptr; // no providing a buffer, should hit rect fallback case
LayerOp op(Rect(10, 10), Matrix4::identity(), nullptr, &layerPaint, &buffer);
testUnmergedGlopDispatch(renderThread, &op,
[](const Glop& glop) { ADD_FAILURE() << "Nothing should happen"; }, 0);
}
static int getGlopTransformFlags(renderthread::RenderThread& renderThread, RecordedOp* op) {
int result = 0;
testUnmergedGlopDispatch(renderThread, op, [&result](const Glop& glop) {
result = glop.transform.transformFlags;
});
return result;
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, offsetFlags) {
Rect bounds(10, 15, 20, 25);
SkPaint paint;
SkPaint aaPaint;
aaPaint.setAntiAlias(true);
RoundRectOp roundRectOp(bounds, Matrix4::identity(), nullptr, &paint, 0, 270);
EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &roundRectOp))
<< "Expect no offset for round rect op.";
const float points[4] = {0.5, 0.5, 1.0, 1.0};
PointsOp antiAliasedPointsOp(bounds, Matrix4::identity(), nullptr, &aaPaint, points, 4);
EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &antiAliasedPointsOp))
<< "Expect no offset for AA points.";
PointsOp pointsOp(bounds, Matrix4::identity(), nullptr, &paint, points, 4);
EXPECT_EQ(TransformFlags::OffsetByFudgeFactor, getGlopTransformFlags(renderThread, &pointsOp))
<< "Expect an offset for non-AA points.";
LinesOp antiAliasedLinesOp(bounds, Matrix4::identity(), nullptr, &aaPaint, points, 4);
EXPECT_EQ(TransformFlags::None, getGlopTransformFlags(renderThread, &antiAliasedLinesOp))
<< "Expect no offset for AA lines.";
LinesOp linesOp(bounds, Matrix4::identity(), nullptr, &paint, points, 4);
EXPECT_EQ(TransformFlags::OffsetByFudgeFactor, getGlopTransformFlags(renderThread, &linesOp))
<< "Expect an offset for non-AA lines.";
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, renderTextWithShadow) {
auto node = TestUtils::createNode<RecordingCanvas>(
0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
android::Paint shadowPaint;
shadowPaint.setColor(SK_ColorRED);
SkScalar sigma = Blur::convertRadiusToSigma(5);
shadowPaint.setLooper(SkBlurDrawLooper::Make(SK_ColorWHITE, sigma, 3, 3));
TestUtils::drawUtf8ToCanvas(&canvas, "A", shadowPaint, 25, 25);
TestUtils::drawUtf8ToCanvas(&canvas, "B", shadowPaint, 50, 50);
});
int glopCount = 0;
auto glopReceiver = [&glopCount](const Glop& glop) {
if (glopCount < 2) {
// two white shadows
EXPECT_EQ(FloatColor({1, 1, 1, 1}), glop.fill.color);
} else {
// two text draws merged into one, drawn after both shadows
EXPECT_EQ(FloatColor({1, 0, 0, 1}), glop.fill.color);
}
glopCount++;
};
ValidatingBakedOpRenderer renderer(renderThread.renderState(), glopReceiver);
FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
Caches::getInstance());
frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
ASSERT_EQ(3, glopCount) << "Exactly three glops expected";
}
static void validateLayerDraw(renderthread::RenderThread& renderThread,
std::function<void(const Glop& glop)> validator) {
auto node = TestUtils::createNode<RecordingCanvas>(
0, 0, 100, 100, [](RenderProperties& props, RecordingCanvas& canvas) {
props.mutateLayerProperties().setType(LayerType::RenderLayer);
// provide different blend mode, so decoration draws contrast
props.mutateLayerProperties().setXferMode(SkBlendMode::kSrc);
canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
});
OffscreenBuffer** layerHandle = node->getLayerHandle();
auto syncedNode = TestUtils::getSyncedNode(node);
// create RenderNode's layer here in same way prepareTree would
OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 100, 100);
*layerHandle = &layer;
{
LayerUpdateQueue layerUpdateQueue; // Note: enqueue damage post-sync, so bounds are valid
layerUpdateQueue.enqueueLayerWithDamage(node.get(), Rect(0, 0, 100, 100));
ValidatingBakedOpRenderer renderer(renderThread.renderState(), validator);
FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometry,
Caches::getInstance());
frameBuilder.deferLayers(layerUpdateQueue);
frameBuilder.deferRenderNode(*syncedNode);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
}
// clean up layer pointer, so we can safely destruct RenderNode
*layerHandle = nullptr;
}
static FloatColor makeFloatColor(uint32_t color) {
FloatColor c;
c.set(color);
return c;
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, layerUpdateProperties) {
for (bool debugOverdraw : {false, true}) {
for (bool debugLayersUpdates : {false, true}) {
ScopedProperty<bool> ovdProp(Properties::debugOverdraw, debugOverdraw);
ScopedProperty<bool> lupProp(Properties::debugLayersUpdates, debugLayersUpdates);
int glopCount = 0;
validateLayerDraw(renderThread, [&glopCount, &debugLayersUpdates](const Glop& glop) {
if (glopCount == 0) {
// 0 - Black layer fill
EXPECT_TRUE(glop.fill.colorEnabled);
EXPECT_EQ(makeFloatColor(Color::Black), glop.fill.color);
} else if (glopCount == 1) {
// 1 - Uncolored (textured) layer draw
EXPECT_FALSE(glop.fill.colorEnabled);
} else if (glopCount == 2) {
// 2 - layer overlay, if present
EXPECT_TRUE(glop.fill.colorEnabled);
// blend srcover, different from that of layer
EXPECT_EQ(GLenum(GL_ONE), glop.blend.src);
EXPECT_EQ(GLenum(GL_ONE_MINUS_SRC_ALPHA), glop.blend.dst);
EXPECT_EQ(makeFloatColor(debugLayersUpdates ? 0x7f00ff00 : 0), glop.fill.color)
<< "Should be transparent green if debugLayersUpdates";
} else if (glopCount < 7) {
// 3 - 6 - overdraw indicator overlays, if present
EXPECT_TRUE(glop.fill.colorEnabled);
uint32_t expectedColor = Caches::getInstance().getOverdrawColor(glopCount - 2);
ASSERT_EQ(makeFloatColor(expectedColor), glop.fill.color);
} else {
ADD_FAILURE() << "Too many glops observed";
}
glopCount++;
});
int expectedCount = 2;
if (debugLayersUpdates || debugOverdraw) expectedCount++;
if (debugOverdraw) expectedCount += 4;
EXPECT_EQ(expectedCount, glopCount);
}
}
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpDispatcher, pathTextureSnapping) {
Rect bounds(10, 15, 20, 25);
SkPaint paint;
SkPath path;
path.addRect(SkRect::MakeXYWH(1.5, 3.8, 100, 90));
PathOp op(bounds, Matrix4::identity(), nullptr, &paint, &path);
testUnmergedGlopDispatch(renderThread, &op, [](const Glop& glop) {
auto texture = glop.fill.texture.texture;
ASSERT_NE(nullptr, texture);
EXPECT_EQ(1, reinterpret_cast<PathTexture*>(texture)->left);
EXPECT_EQ(3, reinterpret_cast<PathTexture*>(texture)->top);
});
}

View File

@@ -1,106 +0,0 @@
/*
* Copyright (C) 2016 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 <BakedOpRenderer.h>
#include <GlopBuilder.h>
#include <tests/common/TestUtils.h>
using namespace android::uirenderer;
const BakedOpRenderer::LightInfo sLightInfo = {128, 128};
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, startRepaintLayer_clear) {
BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(), true, false,
sLightInfo);
OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200u, 200u);
layer.dirty(Rect(200, 200));
{
renderer.startRepaintLayer(&layer, Rect(200, 200));
EXPECT_TRUE(layer.region.isEmpty()) << "Repaint full layer should clear region";
renderer.endLayer();
}
layer.dirty(Rect(200, 200));
{
renderer.startRepaintLayer(&layer, Rect(100, 200)); // repainting left side
EXPECT_TRUE(layer.region.isRect());
// ALOGD("bounds %d %d %d %d", RECT_ARGS(layer.region.getBounds()));
EXPECT_EQ(android::Rect(100, 0, 200, 200), layer.region.getBounds())
<< "Left side being repainted, so right side should be clear";
renderer.endLayer();
}
// right side is now only dirty portion
{
renderer.startRepaintLayer(&layer, Rect(100, 0, 200, 200)); // repainting right side
EXPECT_TRUE(layer.region.isEmpty())
<< "Now right side being repainted, so region should be entirely clear";
renderer.endLayer();
}
}
static void drawFirstOp(RenderState& renderState, int color, SkBlendMode mode) {
BakedOpRenderer renderer(Caches::getInstance(), renderState, true, false, sLightInfo);
renderer.startFrame(100, 100, Rect(100, 100));
SkPaint paint;
paint.setColor(color);
paint.setBlendMode(mode);
Rect dest(0, 0, 100, 100);
Glop glop;
GlopBuilder(renderState, Caches::getInstance(), &glop)
.setRoundRectClipState(nullptr)
.setMeshUnitQuad()
.setFillPaint(paint, 1.0f)
.setTransform(Matrix4::identity(), TransformFlags::None)
.setModelViewMapUnitToRectSnap(dest)
.build();
renderer.renderGlop(nullptr, nullptr, glop);
renderer.endFrame(Rect(100, 100));
}
static void verifyBlend(RenderState& renderState, GLenum expectedSrc, GLenum expectedDst) {
EXPECT_TRUE(renderState.blend().getEnabled());
GLenum src;
GLenum dst;
renderState.blend().getFactors(&src, &dst);
EXPECT_EQ(expectedSrc, src);
EXPECT_EQ(expectedDst, dst);
}
static void verifyBlendDisabled(RenderState& renderState) {
EXPECT_FALSE(renderState.blend().getEnabled());
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, firstDrawBlend_clear) {
// initialize blend state to nonsense value
renderThread.renderState().blend().setFactors(GL_ONE, GL_ONE);
drawFirstOp(renderThread.renderState(), 0xfeff0000, SkBlendMode::kClear);
verifyBlend(renderThread.renderState(), GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(BakedOpRenderer, firstDrawBlend_srcover) {
// initialize blend state to nonsense value
renderThread.renderState().blend().setFactors(GL_ONE, GL_ONE);
drawFirstOp(renderThread.renderState(), 0xfeff0000, SkBlendMode::kSrcOver);
verifyBlendDisabled(renderThread.renderState());
}

View File

@@ -1,266 +0,0 @@
/*
* Copyright (C) 2015 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 <BakedOpState.h>
#include <ClipArea.h>
#include <RecordedOp.h>
#include <tests/common/TestUtils.h>
namespace android {
namespace uirenderer {
TEST(ResolvedRenderState, construct) {
LinearAllocator allocator;
Matrix4 translate10x20;
translate10x20.loadTranslate(10, 20, 0);
SkPaint paint;
ClipRect clip(Rect(100, 200));
RectOp recordedOp(Rect(30, 40, 100, 200), translate10x20, &clip, &paint);
{
// recorded with transform, no parent transform
auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
EXPECT_MATRIX_APPROX_EQ(state.transform, translate10x20);
EXPECT_EQ(Rect(100, 200), state.clipRect());
EXPECT_EQ(Rect(40, 60, 100, 200), state.clippedBounds); // translated and also clipped
EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
}
{
// recorded with transform and parent transform
auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
Matrix4 expectedTranslate;
expectedTranslate.loadTranslate(20, 40, 0);
EXPECT_MATRIX_APPROX_EQ(expectedTranslate, state.transform);
// intersection of parent & transformed child clip
EXPECT_EQ(Rect(10, 20, 100, 200), state.clipRect());
// translated and also clipped
EXPECT_EQ(Rect(50, 80, 100, 200), state.clippedBounds);
EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
}
}
TEST(ResolvedRenderState, computeLocalSpaceClip) {
LinearAllocator allocator;
Matrix4 translate10x20;
translate10x20.loadTranslate(10, 20, 0);
SkPaint paint;
ClipRect clip(Rect(100, 200));
RectOp recordedOp(Rect(1000, 1000), translate10x20, &clip, &paint);
{
// recorded with transform, no parent transform
auto parentSnapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
EXPECT_EQ(Rect(-10, -20, 90, 180), state.computeLocalSpaceClip())
<< "Local clip rect should be 100x200, offset by -10,-20";
}
{
// recorded with transform + parent transform
auto parentSnapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, false, false);
EXPECT_EQ(Rect(-10, -20, 80, 160), state.computeLocalSpaceClip())
<< "Local clip rect should be 90x190, offset by -10,-20";
}
}
const float HAIRLINE = 0.0f;
// Note: bounds will be conservative, but not precise for non-hairline
// - use approx bounds checks for these
const float SEMI_HAIRLINE = 0.3f;
struct StrokeTestCase {
float scale;
float strokeWidth;
const std::function<void(const ResolvedRenderState&)> validator;
};
const static StrokeTestCase sStrokeTestCases[] = {
{1, HAIRLINE,
[](const ResolvedRenderState& state) {
EXPECT_EQ(Rect(49.5f, 49.5f, 150.5f, 150.5f), state.clippedBounds);
}},
{1, SEMI_HAIRLINE,
[](const ResolvedRenderState& state) {
EXPECT_TRUE(state.clippedBounds.contains(49.5f, 49.5f, 150.5f, 150.5f));
EXPECT_TRUE(Rect(49, 49, 151, 151).contains(state.clippedBounds));
}},
{1, 20,
[](const ResolvedRenderState& state) {
EXPECT_EQ(Rect(40, 40, 160, 160), state.clippedBounds);
}},
// 3x3 scale:
{3, HAIRLINE,
[](const ResolvedRenderState& state) {
EXPECT_EQ(Rect(149.5f, 149.5f, 200, 200), state.clippedBounds);
EXPECT_EQ(OpClipSideFlags::Right | OpClipSideFlags::Bottom, state.clipSideFlags);
}},
{3, SEMI_HAIRLINE,
[](const ResolvedRenderState& state) {
EXPECT_TRUE(state.clippedBounds.contains(149.5f, 149.5f, 200, 200));
EXPECT_TRUE(Rect(149, 149, 200, 200).contains(state.clippedBounds));
}},
{3, 20,
[](const ResolvedRenderState& state) {
EXPECT_TRUE(state.clippedBounds.contains(120, 120, 200, 200));
EXPECT_TRUE(Rect(119, 119, 200, 200).contains(state.clippedBounds));
}},
// 0.5f x 0.5f scale
{0.5f, HAIRLINE,
[](const ResolvedRenderState& state) {
EXPECT_EQ(Rect(24.5f, 24.5f, 75.5f, 75.5f), state.clippedBounds);
}},
{0.5f, SEMI_HAIRLINE,
[](const ResolvedRenderState& state) {
EXPECT_TRUE(state.clippedBounds.contains(24.5f, 24.5f, 75.5f, 75.5f));
EXPECT_TRUE(Rect(24, 24, 76, 76).contains(state.clippedBounds));
}},
{0.5f, 20, [](const ResolvedRenderState& state) {
EXPECT_TRUE(state.clippedBounds.contains(19.5f, 19.5f, 80.5f, 80.5f));
EXPECT_TRUE(Rect(19, 19, 81, 81).contains(state.clippedBounds));
}}};
TEST(ResolvedRenderState, construct_expandForStroke) {
LinearAllocator allocator;
// Loop over table of test cases and verify different combinations of stroke width and transform
for (auto&& testCase : sStrokeTestCases) {
SkPaint strokedPaint;
strokedPaint.setAntiAlias(true);
strokedPaint.setStyle(SkPaint::kStroke_Style);
strokedPaint.setStrokeWidth(testCase.strokeWidth);
ClipRect clip(Rect(200, 200));
RectOp recordedOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &strokedPaint);
Matrix4 snapshotMatrix;
snapshotMatrix.loadScale(testCase.scale, testCase.scale, 1);
auto parentSnapshot = TestUtils::makeSnapshot(snapshotMatrix, Rect(200, 200));
ResolvedRenderState state(allocator, *parentSnapshot, recordedOp, true, false);
testCase.validator(state);
}
}
TEST(BakedOpState, tryConstruct) {
Matrix4 translate100x0;
translate100x0.loadTranslate(100, 0, 0);
SkPaint paint;
ClipRect clip(Rect(100, 200));
LinearAllocator allocator;
RectOp successOp(Rect(30, 40, 100, 200), Matrix4::identity(), &clip, &paint);
auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(100, 200));
EXPECT_NE(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, successOp))
<< "successOp NOT rejected by clip, so should be constructed";
size_t successAllocSize = allocator.usedSize();
EXPECT_LE(64u, successAllocSize) << "relatively large alloc for non-rejected op";
RectOp rejectOp(Rect(30, 40, 100, 200), translate100x0, &clip, &paint);
EXPECT_EQ(nullptr, BakedOpState::tryConstruct(allocator, *snapshot, rejectOp))
<< "rejectOp rejected by clip, so should not be constructed";
// NOTE: this relies on the clip having already been serialized by the op above
EXPECT_EQ(successAllocSize, allocator.usedSize()) << "no extra allocation used for rejected op";
}
TEST(BakedOpState, tryShadowOpConstruct) {
Matrix4 translate10x20;
translate10x20.loadTranslate(10, 20, 0);
LinearAllocator allocator;
{
auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect()); // Note: empty clip
BakedOpState* bakedState =
BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
EXPECT_EQ(nullptr, bakedState) << "op should be rejected by clip, so not constructed";
EXPECT_EQ(0u, allocator.usedSize()) << "no serialization, even for clip,"
"since op is quick rejected based on snapshot clip";
}
{
auto snapshot = TestUtils::makeSnapshot(translate10x20, Rect(100, 200));
BakedOpState* bakedState =
BakedOpState::tryShadowOpConstruct(allocator, *snapshot, (ShadowOp*)0x1234);
ASSERT_NE(nullptr, bakedState) << "NOT rejected by clip, so op should be constructed";
EXPECT_LE(64u, allocator.usedSize()) << "relatively large alloc for non-rejected op";
EXPECT_MATRIX_APPROX_EQ(translate10x20, bakedState->computedState.transform);
EXPECT_EQ(Rect(100, 200), bakedState->computedState.clippedBounds);
}
}
TEST(BakedOpState, tryStrokeableOpConstruct) {
LinearAllocator allocator;
{
// check regular rejection
SkPaint paint;
paint.setStyle(SkPaint::kStrokeAndFill_Style);
paint.setStrokeWidth(0.0f);
ClipRect clip(Rect(100, 200));
RectOp rejectOp(Rect(100, 200), Matrix4::identity(), &clip, &paint);
auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect()); // Note: empty clip
auto bakedState = BakedOpState::tryStrokeableOpConstruct(
allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::StyleDefined, false);
EXPECT_EQ(nullptr, bakedState);
EXPECT_GT(8u,
allocator.usedSize()); // no significant allocation space used for rejected op
}
{
// check simple unscaled expansion
SkPaint paint;
paint.setStyle(SkPaint::kStrokeAndFill_Style);
paint.setStrokeWidth(10.0f);
ClipRect clip(Rect(200, 200));
RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
auto bakedState = BakedOpState::tryStrokeableOpConstruct(
allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::StyleDefined, false);
ASSERT_NE(nullptr, bakedState);
EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
}
{
// check simple unscaled expansion, and fill style with stroke forced
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
paint.setStrokeWidth(10.0f);
ClipRect clip(Rect(200, 200));
RectOp rejectOp(Rect(50, 50, 150, 150), Matrix4::identity(), &clip, &paint);
auto snapshot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(200, 200));
auto bakedState = BakedOpState::tryStrokeableOpConstruct(
allocator, *snapshot, rejectOp, BakedOpState::StrokeBehavior::Forced, false);
ASSERT_NE(nullptr, bakedState);
EXPECT_EQ(Rect(45, 45, 155, 155), bakedState->computedState.clippedBounds);
EXPECT_EQ(0, bakedState->computedState.clipSideFlags);
}
}
} // namespace uirenderer
} // namespace android

View File

@@ -1,31 +0,0 @@
/*
* Copyright (C) 2015 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 <DeviceInfo.h>
#include <gtest/gtest.h>
#include "tests/common/TestUtils.h"
using namespace android;
using namespace android::uirenderer;
OPENGL_PIPELINE_TEST(DeviceInfo, basic) {
// can't assert state before init - another test may have initialized the singleton
DeviceInfo::initialize();
const DeviceInfo* di = DeviceInfo::get();
ASSERT_NE(nullptr, di) << "DeviceInfo initialization failed";
EXPECT_EQ(2048, di->maxTextureSize()) << "Max texture size didn't match";
}

View File

@@ -1,55 +0,0 @@
/*
* Copyright (C) 2016 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 "GammaFontRenderer.h"
#include "tests/common/TestUtils.h"
using namespace android::uirenderer;
static bool isZero(uint8_t* data, int size) {
for (int i = 0; i < size; i++) {
if (data[i]) return false;
}
return true;
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, renderDropShadow) {
SkPaint paint;
paint.setTextSize(10);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
GammaFontRenderer gammaFontRenderer;
FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
fontRenderer.setFont(&paint, SkMatrix::I());
std::vector<glyph_t> glyphs;
std::vector<float> positions;
float totalAdvance;
Rect bounds;
TestUtils::layoutTextUnscaled(paint, "This is a test", &glyphs, &positions, &totalAdvance,
&bounds);
for (int radius : {28, 20, 2}) {
auto result = fontRenderer.renderDropShadow(&paint, glyphs.data(), glyphs.size(), radius,
positions.data());
ASSERT_NE(nullptr, result.image);
EXPECT_FALSE(isZero(result.image, result.width * result.height));
EXPECT_LE(bounds.getWidth() + radius * 2, (int)result.width);
EXPECT_LE(bounds.getHeight() + radius * 2, (int)result.height);
delete result.image;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,145 +0,0 @@
/*
* Copyright (C) 2016 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 "Glop.h"
#include "GlopBuilder.h"
#include "Rect.h"
#include "tests/common/TestUtils.h"
#include "utils/Color.h"
#include <SkPaint.h>
using namespace android::uirenderer;
static void expectFillEq(Glop::Fill& expectedFill, Glop::Fill& builtFill) {
EXPECT_EQ(expectedFill.colorEnabled, builtFill.colorEnabled);
if (expectedFill.colorEnabled) EXPECT_EQ(expectedFill.color, builtFill.color);
EXPECT_EQ(expectedFill.filterMode, builtFill.filterMode);
if (expectedFill.filterMode == ProgramDescription::ColorFilterMode::Blend) {
EXPECT_EQ(expectedFill.filter.color, builtFill.filter.color);
} else if (expectedFill.filterMode == ProgramDescription::ColorFilterMode::Matrix) {
Glop::Fill::Filter::Matrix& expectedMatrix = expectedFill.filter.matrix;
Glop::Fill::Filter::Matrix& builtMatrix = expectedFill.filter.matrix;
EXPECT_TRUE(std::memcmp(expectedMatrix.matrix, builtMatrix.matrix,
sizeof(Glop::Fill::Filter::Matrix::matrix)));
EXPECT_TRUE(std::memcmp(expectedMatrix.vector, builtMatrix.vector,
sizeof(Glop::Fill::Filter::Matrix::vector)));
}
EXPECT_EQ(expectedFill.skiaShaderData.skiaShaderType, builtFill.skiaShaderData.skiaShaderType);
EXPECT_EQ(expectedFill.texture.clamp, builtFill.texture.clamp);
EXPECT_EQ(expectedFill.texture.filter, builtFill.texture.filter);
EXPECT_TRUE((expectedFill.texture.texture && builtFill.texture.texture) ||
(!expectedFill.texture.texture && !builtFill.texture.texture));
if (expectedFill.texture.texture) {
EXPECT_EQ(expectedFill.texture.texture->target(), builtFill.texture.texture->target());
}
EXPECT_EQ(expectedFill.texture.textureTransform, builtFill.texture.textureTransform);
}
static void expectBlendEq(Glop::Blend& expectedBlend, Glop::Blend& builtBlend) {
EXPECT_EQ(expectedBlend.src, builtBlend.src);
EXPECT_EQ(expectedBlend.dst, builtBlend.dst);
}
static void expectMeshEq(Glop::Mesh& expectedMesh, Glop::Mesh& builtMesh) {
EXPECT_EQ(expectedMesh.elementCount, builtMesh.elementCount);
EXPECT_EQ(expectedMesh.primitiveMode, builtMesh.primitiveMode);
EXPECT_EQ(expectedMesh.indices.indices, builtMesh.indices.indices);
EXPECT_EQ(expectedMesh.indices.bufferObject, builtMesh.indices.bufferObject);
EXPECT_EQ(expectedMesh.vertices.attribFlags, builtMesh.vertices.attribFlags);
EXPECT_EQ(expectedMesh.vertices.bufferObject, builtMesh.vertices.bufferObject);
EXPECT_EQ(expectedMesh.vertices.color, builtMesh.vertices.color);
EXPECT_EQ(expectedMesh.vertices.position, builtMesh.vertices.position);
EXPECT_EQ(expectedMesh.vertices.stride, builtMesh.vertices.stride);
EXPECT_EQ(expectedMesh.vertices.texCoord, builtMesh.vertices.texCoord);
if (builtMesh.vertices.position) {
for (int i = 0; i < 4; i++) {
TextureVertex& expectedVertex = expectedMesh.mappedVertices[i];
TextureVertex& builtVertex = builtMesh.mappedVertices[i];
EXPECT_EQ(expectedVertex.u, builtVertex.u);
EXPECT_EQ(expectedVertex.v, builtVertex.v);
EXPECT_EQ(expectedVertex.x, builtVertex.x);
EXPECT_EQ(expectedVertex.y, builtVertex.y);
}
}
}
static void expectTransformEq(Glop::Transform& expectedTransform, Glop::Transform& builtTransform) {
EXPECT_EQ(expectedTransform.canvas, builtTransform.canvas);
EXPECT_EQ(expectedTransform.modelView, builtTransform.modelView);
EXPECT_EQ(expectedTransform.transformFlags, expectedTransform.transformFlags);
}
static void expectGlopEq(Glop& expectedGlop, Glop& builtGlop) {
expectBlendEq(expectedGlop.blend, builtGlop.blend);
expectFillEq(expectedGlop.fill, builtGlop.fill);
expectMeshEq(expectedGlop.mesh, builtGlop.mesh);
expectTransformEq(expectedGlop.transform, builtGlop.transform);
}
static std::unique_ptr<Glop> blackUnitQuadGlop(RenderState& renderState) {
std::unique_ptr<Glop> glop(new Glop());
glop->blend = {GL_ZERO, GL_ZERO};
glop->mesh.elementCount = 4;
glop->mesh.primitiveMode = GL_TRIANGLE_STRIP;
glop->mesh.indices.indices = nullptr;
glop->mesh.indices.bufferObject = GL_ZERO;
glop->mesh.vertices = {renderState.meshState().getUnitQuadVBO(),
VertexAttribFlags::None,
nullptr,
nullptr,
nullptr,
kTextureVertexStride};
glop->transform.modelView.loadIdentity();
glop->fill.colorEnabled = true;
glop->fill.color.set(Color::Black);
glop->fill.skiaShaderData.skiaShaderType = kNone_SkiaShaderType;
glop->fill.filterMode = ProgramDescription::ColorFilterMode::None;
glop->fill.texture = {nullptr, GL_INVALID_ENUM, GL_INVALID_ENUM, nullptr};
return glop;
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(GlopBuilder, rectSnapTest) {
RenderState& renderState = renderThread.renderState();
Caches& caches = Caches::getInstance();
SkPaint paint;
Rect dest(1, 1, 100, 100);
Matrix4 simpleTranslate;
simpleTranslate.loadTranslate(0.7, 0.7, 0);
Glop glop;
GlopBuilder(renderState, caches, &glop)
.setRoundRectClipState(nullptr)
.setMeshUnitQuad()
.setFillPaint(paint, 1.0f)
.setTransform(simpleTranslate, TransformFlags::None)
.setModelViewMapUnitToRectSnap(dest)
.build();
std::unique_ptr<Glop> goldenGlop(blackUnitQuadGlop(renderState));
// Rect(1,1,100,100) is the set destination,
// so unit quad should be translated by (1,1) and scaled by (99, 99)
// Tricky part: because translate (0.7, 0.7) and snapping were set in glopBuilder,
// unit quad also should be translate by additional (0.3, 0.3) to snap to exact pixels.
goldenGlop->transform.modelView.loadTranslate(1.3, 1.3, 0);
goldenGlop->transform.modelView.scale(99, 99, 1);
goldenGlop->transform.canvas = simpleTranslate;
goldenGlop->fill.texture.filter = GL_NEAREST;
expectGlopEq(*goldenGlop, glop);
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (C) 2016 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 "Extensions.h"
#include "GradientCache.h"
#include "tests/common/TestUtils.h"
using namespace android;
using namespace android::uirenderer;
RENDERTHREAD_OPENGL_PIPELINE_TEST(GradientCache, addRemove) {
Extensions extensions;
GradientCache cache(extensions);
ASSERT_LT(1000u, cache.getMaxSize()) << "Expect non-trivial size";
SkColor colors[] = {0xFF00FF00, 0xFFFF0000, 0xFF0000FF};
float positions[] = {1, 2, 3};
Texture* texture = cache.get(colors, positions, 3);
ASSERT_TRUE(texture);
ASSERT_FALSE(texture->cleanup);
ASSERT_EQ((uint32_t)texture->objectSize(), cache.getSize());
ASSERT_TRUE(cache.getSize());
cache.clear();
ASSERT_EQ(cache.getSize(), 0u);
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright (C) 2016 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 "BakedOpDispatcher.h"
#include "BakedOpRenderer.h"
#include "FrameBuilder.h"
#include "LayerUpdateQueue.h"
#include "RecordingCanvas.h"
#include "tests/common/TestUtils.h"
#include <gtest/gtest.h>
using namespace android;
using namespace android::uirenderer;
const FrameBuilder::LightGeometry sLightGeometery = {{100, 100, 100}, 50};
const BakedOpRenderer::LightInfo sLightInfo = {128, 128};
RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayer_overdrawRejection) {
auto node = TestUtils::createNode(0, 0, 100, 100, [](RenderProperties& props, Canvas& canvas) {
canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 100, 100, SkPaint());
canvas.restore();
// opaque draw, rejects saveLayer beneath
canvas.drawRect(0, 0, 100, 100, SkPaint());
});
RenderState& renderState = renderThread.renderState();
Caches& caches = Caches::getInstance();
FrameBuilder frameBuilder(SkRect::MakeWH(100, 100), 100, 100, sLightGeometery,
Caches::getInstance());
frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(LeakCheck, saveLayerUnclipped_simple) {
auto node = TestUtils::createNode(0, 0, 200, 200, [](RenderProperties& props, Canvas& canvas) {
canvas.saveLayerAlpha(10, 10, 190, 190, 128, (SaveFlags::Flags)(0));
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.restore();
});
RenderState& renderState = renderThread.renderState();
Caches& caches = Caches::getInstance();
FrameBuilder frameBuilder(SkRect::MakeWH(200, 200), 200, 200, sLightGeometery,
Caches::getInstance());
frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
BakedOpRenderer renderer(caches, renderState, true, false, sLightInfo);
frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer);
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright (C) 2016 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 <debug/MockGlesDriver.h>
#include <debug/ScopedReplaceDriver.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <renderstate/MeshState.h>
#include <tests/common/TestUtils.h>
using namespace android::uirenderer;
using namespace testing;
RENDERTHREAD_OPENGL_PIPELINE_TEST(MeshState, genOrUpdate) {
debug::ScopedReplaceDriver<debug::MockGlesDriver> driverRef;
auto& mockGlDriver = driverRef.get();
EXPECT_CALL(mockGlDriver, glGenBuffers_(_, _)).WillOnce(SetArgPointee<1>(35));
EXPECT_CALL(mockGlDriver, glBindBuffer_(_, 35));
EXPECT_CALL(mockGlDriver, glBufferData_(_, _, _, _));
GLuint buffer = 0;
renderThread.renderState().meshState().genOrUpdateMeshBuffer(&buffer, 10, nullptr,
GL_DYNAMIC_DRAW);
}

View File

@@ -1,244 +0,0 @@
/*
* Copyright (C) 2015 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 <Rect.h>
#include <gtest/gtest.h>
#include <renderstate/OffscreenBufferPool.h>
#include <tests/common/TestUtils.h>
using namespace android::uirenderer;
TEST(OffscreenBuffer, computeIdealDimension) {
EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(1));
EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(31));
EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(33));
EXPECT_EQ(64u, OffscreenBuffer::computeIdealDimension(64));
EXPECT_EQ(1024u, OffscreenBuffer::computeIdealDimension(1000));
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, construct) {
OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 49u, 149u);
EXPECT_EQ(49u, layer.viewportWidth);
EXPECT_EQ(149u, layer.viewportHeight);
EXPECT_EQ(64u, layer.texture.width());
EXPECT_EQ(192u, layer.texture.height());
EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes());
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, constructWideColorGamut) {
OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 49u, 149u, true);
EXPECT_EQ(49u, layer.viewportWidth);
EXPECT_EQ(149u, layer.viewportHeight);
EXPECT_EQ(64u, layer.texture.width());
EXPECT_EQ(192u, layer.texture.height());
EXPECT_TRUE(layer.wideColorGamut);
EXPECT_EQ(64u * 192u * 8u, layer.getSizeInBytes());
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, getTextureCoordinates) {
OffscreenBuffer layerAligned(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
EXPECT_EQ(Rect(0, 1, 1, 0), layerAligned.getTextureCoordinates());
OffscreenBuffer layerUnaligned(renderThread.renderState(), Caches::getInstance(), 200u, 225u);
EXPECT_EQ(Rect(0, 225.0f / 256.0f, 200.0f / 256.0f, 0), layerUnaligned.getTextureCoordinates());
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBuffer, dirty) {
OffscreenBuffer buffer(renderThread.renderState(), Caches::getInstance(), 256u, 256u);
buffer.dirty(Rect(-100, -100, 100, 100));
EXPECT_EQ(android::Rect(100, 100), buffer.region.getBounds());
}
RENDERTHREAD_TEST(OffscreenBufferPool, construct) {
OffscreenBufferPool pool;
EXPECT_EQ(0u, pool.getCount()) << "pool must be created empty";
EXPECT_EQ(0u, pool.getSize()) << "pool must be created empty";
// TODO: Does this really make sense as a test?
EXPECT_EQ(DeviceInfo::multiplyByResolution(4 * 4), pool.getMaxSize());
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, getPutClear) {
OffscreenBufferPool pool;
auto layer = pool.get(renderThread.renderState(), 100u, 200u);
EXPECT_EQ(100u, layer->viewportWidth);
EXPECT_EQ(200u, layer->viewportHeight);
ASSERT_LT(layer->getSizeInBytes(), pool.getMaxSize());
pool.putOrDelete(layer);
ASSERT_EQ(layer->getSizeInBytes(), pool.getSize());
auto layer2 = pool.get(renderThread.renderState(), 102u, 202u);
EXPECT_EQ(layer, layer2) << "layer should be recycled";
ASSERT_EQ(0u, pool.getSize()) << "pool should have been emptied by removing only layer";
pool.putOrDelete(layer);
EXPECT_EQ(1u, pool.getCount());
pool.clear();
EXPECT_EQ(0u, pool.getSize());
EXPECT_EQ(0u, pool.getCount());
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, getPutClearWideColorGamut) {
OffscreenBufferPool pool;
auto layer = pool.get(renderThread.renderState(), 100u, 200u, true);
EXPECT_EQ(100u, layer->viewportWidth);
EXPECT_EQ(200u, layer->viewportHeight);
EXPECT_TRUE(layer->wideColorGamut);
ASSERT_LT(layer->getSizeInBytes(), pool.getMaxSize());
pool.putOrDelete(layer);
ASSERT_EQ(layer->getSizeInBytes(), pool.getSize());
auto layer2 = pool.get(renderThread.renderState(), 102u, 202u, true);
EXPECT_EQ(layer, layer2) << "layer should be recycled";
ASSERT_EQ(0u, pool.getSize()) << "pool should have been emptied by removing only layer";
pool.putOrDelete(layer2);
EXPECT_EQ(1u, pool.getCount());
pool.clear();
EXPECT_EQ(0u, pool.getSize());
EXPECT_EQ(0u, pool.getCount());
// add non wide gamut layer
auto layer3 = pool.get(renderThread.renderState(), 100u, 200u);
EXPECT_FALSE(layer3->wideColorGamut);
pool.putOrDelete(layer3);
EXPECT_EQ(1u, pool.getCount());
auto layer4 = pool.get(renderThread.renderState(), 100u, 200u, true);
EXPECT_TRUE(layer4->wideColorGamut);
EXPECT_EQ(1u, pool.getCount());
ASSERT_NE(layer3, layer4);
pool.putOrDelete(layer4);
pool.clear();
EXPECT_EQ(0u, pool.getSize());
EXPECT_EQ(0u, pool.getCount());
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, resize) {
OffscreenBufferPool pool;
auto layer = pool.get(renderThread.renderState(), 64u, 64u);
layer->dirty(Rect(64, 64));
// resize in place
ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
EXPECT_TRUE(layer->region.isEmpty()) << "In place resize should clear usage region";
EXPECT_EQ(60u, layer->viewportWidth);
EXPECT_EQ(55u, layer->viewportHeight);
EXPECT_EQ(64u, layer->texture.width());
EXPECT_EQ(64u, layer->texture.height());
// resized to use different object in pool
auto layer2 = pool.get(renderThread.renderState(), 128u, 128u);
layer2->dirty(Rect(128, 128));
EXPECT_FALSE(layer2->region.isEmpty());
pool.putOrDelete(layer2);
ASSERT_EQ(1u, pool.getCount());
ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
EXPECT_TRUE(layer2->region.isEmpty()) << "Swap resize should clear usage region";
EXPECT_EQ(120u, layer2->viewportWidth);
EXPECT_EQ(125u, layer2->viewportHeight);
EXPECT_EQ(128u, layer2->texture.width());
EXPECT_EQ(128u, layer2->texture.height());
// original allocation now only thing in pool
EXPECT_EQ(1u, pool.getCount());
EXPECT_EQ(layer->getSizeInBytes(), pool.getSize());
pool.putOrDelete(layer2);
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, resizeWideColorGamut) {
OffscreenBufferPool pool;
auto layer = pool.get(renderThread.renderState(), 64u, 64u, true);
// resize in place
ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
EXPECT_EQ(60u, layer->viewportWidth);
EXPECT_EQ(55u, layer->viewportHeight);
EXPECT_EQ(64u, layer->texture.width());
EXPECT_EQ(64u, layer->texture.height());
EXPECT_TRUE(layer->wideColorGamut);
EXPECT_EQ(64u * 64u * 8u, layer->getSizeInBytes());
// resized to use different object in pool
auto layer2 = pool.get(renderThread.renderState(), 128u, 128u, true);
pool.putOrDelete(layer2);
ASSERT_EQ(1u, pool.getCount());
// add a non-wide gamut layer
auto layer3 = pool.get(renderThread.renderState(), 128u, 128u);
pool.putOrDelete(layer3);
ASSERT_EQ(2u, pool.getCount());
ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
EXPECT_EQ(120u, layer2->viewportWidth);
EXPECT_EQ(125u, layer2->viewportHeight);
EXPECT_EQ(128u, layer2->texture.width());
EXPECT_EQ(128u, layer2->texture.height());
EXPECT_TRUE(layer2->wideColorGamut);
EXPECT_EQ(128u * 128u * 8u, layer2->getSizeInBytes());
pool.putOrDelete(layer2);
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, putAndDestroy) {
OffscreenBufferPool pool;
// layer too big to return to the pool
// Note: this relies on the fact that the pool won't reject based on max texture size
auto hugeLayer = pool.get(renderThread.renderState(), pool.getMaxSize() / 64, 64);
EXPECT_GT(hugeLayer->getSizeInBytes(), pool.getMaxSize());
pool.putOrDelete(hugeLayer);
EXPECT_EQ(0u, pool.getCount()); // failed to put (so was destroyed instead)
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(OffscreenBufferPool, clear) {
EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
OffscreenBufferPool pool;
// Create many buffers, with several at each size
std::vector<OffscreenBuffer*> buffers;
for (int size = 32; size <= 128; size += 32) {
for (int i = 0; i < 10; i++) {
buffers.push_back(pool.get(renderThread.renderState(), size, size));
}
}
EXPECT_EQ(0u, pool.getCount()) << "Expect nothing inside";
for (auto& buffer : buffers) pool.putOrDelete(buffer);
EXPECT_EQ(40u, pool.getCount()) << "Expect all items added";
EXPECT_EQ(40, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
pool.clear();
EXPECT_EQ(0u, pool.getCount()) << "Expect all items cleared";
EXPECT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::OffscreenBuffer));
}

View File

@@ -1,847 +0,0 @@
/*
* Copyright (C) 2015 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 <DeferredLayerUpdater.h>
#include <RecordedOp.h>
#include <RecordingCanvas.h>
#include <hwui/Paint.h>
#include <minikin/Layout.h>
#include <tests/common/TestUtils.h>
#include <utils/Color.h>
#include <SkGradientShader.h>
#include <SkImagePriv.h>
#include <SkShader.h>
namespace android {
namespace uirenderer {
static void playbackOps(const DisplayList& displayList,
std::function<void(const RecordedOp&)> opReceiver) {
for (auto& chunk : displayList.getChunks()) {
for (size_t opIndex = chunk.beginOpIndex; opIndex < chunk.endOpIndex; opIndex++) {
RecordedOp* op = displayList.getOps()[opIndex];
opReceiver(*op);
}
}
}
static void validateSingleOp(std::unique_ptr<DisplayList>& dl,
std::function<void(const RecordedOp& op)> opValidator) {
ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
opValidator(*(dl->getOps()[0]));
}
// The RecordingCanvas is only ever used by the OpenGL RenderPipeline and never when Skia is in use.
// Thus, even though many of these test are not directly dependent on the current RenderPipeline, we
// set them all to be OPENGL_PIPELINE_TESTs in case the underlying code in RecordingCanvas ever
// changes to require the use of the OPENGL_PIPELINE. Currently the textureLayer test is the only
// test that requires being an OPENGL_PIPELINE_TEST.
OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPlayback) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.restore();
});
playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); });
}
OPENGL_PIPELINE_TEST(RecordingCanvas, clipRect) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
canvas.drawRect(0, 0, 50, 50, SkPaint());
canvas.drawRect(50, 50, 100, 100, SkPaint());
canvas.restore();
});
ASSERT_EQ(2u, dl->getOps().size()) << "Must be exactly two ops";
EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[0]->localClip);
EXPECT_CLIP_RECT(Rect(100, 100), dl->getOps()[1]->localClip);
EXPECT_EQ(dl->getOps()[0]->localClip, dl->getOps()[1]->localClip)
<< "Clip should be serialized once";
}
OPENGL_PIPELINE_TEST(RecordingCanvas, emptyClipRect) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(0, 0, 100, 100, SkClipOp::kIntersect);
canvas.clipRect(100, 100, 200, 200, SkClipOp::kIntersect);
canvas.drawRect(0, 0, 50, 50, SkPaint()); // rejected at record time
canvas.restore();
});
ASSERT_EQ(0u, dl->getOps().size()) << "Must be zero ops. Rect should be rejected.";
}
OPENGL_PIPELINE_TEST(RecordingCanvas, emptyPaintRejection) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkPaint emptyPaint;
emptyPaint.setColor(Color::Transparent);
float points[] = {0, 0, 200, 200};
canvas.drawPoints(points, 4, emptyPaint);
canvas.drawLines(points, 4, emptyPaint);
canvas.drawRect(0, 0, 200, 200, emptyPaint);
canvas.drawRegion(SkRegion(SkIRect::MakeWH(200, 200)), emptyPaint);
canvas.drawRoundRect(0, 0, 200, 200, 10, 10, emptyPaint);
canvas.drawCircle(100, 100, 100, emptyPaint);
canvas.drawOval(0, 0, 200, 200, emptyPaint);
canvas.drawArc(0, 0, 200, 200, 0, 360, true, emptyPaint);
SkPath path;
path.addRect(0, 0, 200, 200);
canvas.drawPath(path, emptyPaint);
});
EXPECT_EQ(0u, dl->getOps().size()) << "Op should be rejected";
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawArc) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.drawArc(0, 0, 200, 200, 0, 180, true, SkPaint());
canvas.drawArc(0, 0, 100, 100, 0, 360, true, SkPaint());
});
auto&& ops = dl->getOps();
ASSERT_EQ(2u, ops.size()) << "Must be exactly two ops";
EXPECT_EQ(RecordedOpId::ArcOp, ops[0]->opId);
EXPECT_EQ(Rect(200, 200), ops[0]->unmappedBounds);
EXPECT_EQ(RecordedOpId::OvalOp, ops[1]->opId) << "Circular arcs should be converted to ovals";
EXPECT_EQ(Rect(100, 100), ops[1]->unmappedBounds);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawLines) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setStrokeWidth(
20); // doesn't affect recorded bounds - would be resolved at bake time
float points[] = {0, 0, 20, 10, 30, 40, 90}; // NB: only 1 valid line
canvas.drawLines(&points[0], 7, paint);
});
ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
auto op = dl->getOps()[0];
ASSERT_EQ(RecordedOpId::LinesOp, op->opId);
EXPECT_EQ(4, ((LinesOp*)op)->floatCount)
<< "float count must be rounded down to closest multiple of 4";
EXPECT_EQ(Rect(20, 10), op->unmappedBounds)
<< "unmapped bounds must be size of line, and not outset for stroke width";
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawRect) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(
100, 200, [](RecordingCanvas& canvas) { canvas.drawRect(10, 20, 90, 180, SkPaint()); });
ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
auto op = *(dl->getOps()[0]);
ASSERT_EQ(RecordedOpId::RectOp, op.opId);
EXPECT_EQ(nullptr, op.localClip);
EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawRoundRect) {
// Round case - stays rounded
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
canvas.drawRoundRect(0, 0, 100, 100, 10, 10, SkPaint());
});
ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
ASSERT_EQ(RecordedOpId::RoundRectOp, dl->getOps()[0]->opId);
// Non-rounded case - turned into drawRect
dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
canvas.drawRoundRect(0, 0, 100, 100, 0, -1, SkPaint());
});
ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
ASSERT_EQ(RecordedOpId::RectOp, dl->getOps()[0]->opId)
<< "Non-rounded rects should be converted";
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(20);
TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
});
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
count++;
ASSERT_EQ(RecordedOpId::TextOp, op.opId);
EXPECT_EQ(nullptr, op.localClip);
EXPECT_TRUE(op.localMatrix.isIdentity());
EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25))
<< "Op expected to be 25+ pixels wide, 10+ pixels tall";
});
ASSERT_EQ(1, count);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_strikeThruAndUnderline) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(20);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
uint32_t flags = paint.getFlags();
if (i != 0) {
flags |= SkPaint::kUnderlineText_ReserveFlag;
} else {
flags &= ~SkPaint::kUnderlineText_ReserveFlag;
}
if (j != 0) {
flags |= SkPaint::kStrikeThruText_ReserveFlag;
} else {
flags &= ~SkPaint::kStrikeThruText_ReserveFlag;
}
paint.setFlags(flags);
TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
}
}
});
auto ops = dl->getOps();
ASSERT_EQ(8u, ops.size());
int index = 0;
EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough
EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only
EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only
EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId);
EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline
EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawGlyphs_forceAlignLeft) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(20);
paint.setTextAlign(SkPaint::kLeft_Align);
TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
paint.setTextAlign(SkPaint::kCenter_Align);
TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
paint.setTextAlign(SkPaint::kRight_Align);
TestUtils::drawUtf8ToCanvas(&canvas, "test text", paint, 25, 25);
});
int count = 0;
float lastX = FLT_MAX;
playbackOps(*dl, [&count, &lastX](const RecordedOp& op) {
count++;
ASSERT_EQ(RecordedOpId::TextOp, op.opId);
EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign())
<< "recorded drawText commands must force kLeft_Align on their paint";
// verify TestUtils alignment offsetting (TODO: move asserts to Canvas base class)
EXPECT_GT(lastX, ((const TextOp&)op).x)
<< "x coordinate should reduce across each of the draw commands, from alignment";
lastX = ((const TextOp&)op).x;
});
ASSERT_EQ(3, count);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawColor) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.drawColor(Color::Black, SkBlendMode::kSrcOver);
});
ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op";
auto op = *(dl->getOps()[0]);
EXPECT_EQ(RecordedOpId::ColorOp, op.opId);
EXPECT_EQ(nullptr, op.localClip);
EXPECT_TRUE(op.unmappedBounds.isEmpty()) << "Expect undefined recorded bounds";
}
OPENGL_PIPELINE_TEST(RecordingCanvas, backgroundAndImage) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 200, [](RecordingCanvas& canvas) {
sk_sp<Bitmap> bitmap(TestUtils::createBitmap(25, 25));
SkPaint paint;
paint.setColor(SK_ColorBLUE);
canvas.save(SaveFlags::MatrixClip);
{
// a background!
canvas.save(SaveFlags::MatrixClip);
canvas.drawRect(0, 0, 100, 200, paint);
canvas.restore();
}
{
// an image!
canvas.save(SaveFlags::MatrixClip);
canvas.translate(25, 25);
canvas.scale(2, 2);
canvas.drawBitmap(*bitmap, 0, 0, nullptr);
canvas.restore();
}
canvas.restore();
});
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
if (count == 0) {
ASSERT_EQ(RecordedOpId::RectOp, op.opId);
ASSERT_NE(nullptr, op.paint);
EXPECT_EQ(SK_ColorBLUE, op.paint->getColor());
EXPECT_EQ(Rect(100, 200), op.unmappedBounds);
EXPECT_EQ(nullptr, op.localClip);
Matrix4 expectedMatrix;
expectedMatrix.loadIdentity();
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
} else {
ASSERT_EQ(RecordedOpId::BitmapOp, op.opId);
EXPECT_EQ(nullptr, op.paint);
EXPECT_EQ(Rect(25, 25), op.unmappedBounds);
EXPECT_EQ(nullptr, op.localClip);
Matrix4 expectedMatrix;
expectedMatrix.loadTranslate(25, 25, 0);
expectedMatrix.scale(2, 2, 1);
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
}
count++;
});
ASSERT_EQ(2, count);
}
RENDERTHREAD_OPENGL_PIPELINE_TEST(RecordingCanvas, textureLayer) {
auto layerUpdater =
TestUtils::createTextureLayerUpdater(renderThread, 100, 100, SkMatrix::MakeTrans(5, 5));
auto dl = TestUtils::createDisplayList<RecordingCanvas>(
200, 200,
[&layerUpdater](RecordingCanvas& canvas) { canvas.drawLayer(layerUpdater.get()); });
validateSingleOp(dl, [](const RecordedOp& op) {
ASSERT_EQ(RecordedOpId::TextureLayerOp, op.opId);
ASSERT_TRUE(op.localMatrix.isIdentity()) << "Op must not apply matrix at record time.";
});
}
OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simple) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10, 20, 190, 180, 128, SaveFlags::ClipToLayer);
canvas.drawRect(10, 20, 190, 180, SkPaint());
canvas.restore();
});
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
Matrix4 expectedMatrix;
switch (count++) {
case 0:
EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
EXPECT_EQ(nullptr, op.localClip);
EXPECT_TRUE(op.localMatrix.isIdentity());
break;
case 1:
EXPECT_EQ(RecordedOpId::RectOp, op.opId);
EXPECT_CLIP_RECT(Rect(180, 160), op.localClip);
EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
expectedMatrix.loadTranslate(-10, -20, 0);
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
break;
case 2:
EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
// Don't bother asserting recording state data - it's not used
break;
default:
ADD_FAILURE();
}
});
EXPECT_EQ(3, count);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rounding) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10.25f, 10.75f, 89.25f, 89.75f, 128, SaveFlags::ClipToLayer);
canvas.drawRect(20, 20, 80, 80, SkPaint());
canvas.restore();
});
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
Matrix4 expectedMatrix;
switch (count++) {
case 0:
EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId);
EXPECT_EQ(Rect(10, 10, 90, 90), op.unmappedBounds) << "Expect bounds rounded out";
break;
case 1:
EXPECT_EQ(RecordedOpId::RectOp, op.opId);
expectedMatrix.loadTranslate(-10, -10, 0);
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix) << "Expect rounded offset";
break;
case 2:
EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
// Don't bother asserting recording state data - it's not used
break;
default:
ADD_FAILURE();
}
});
EXPECT_EQ(3, count);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_missingRestore) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 200, 200, SkPaint());
// Note: restore omitted, shouldn't result in unmatched save
});
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
if (count++ == 2) {
EXPECT_EQ(RecordedOpId::EndLayerOp, op.opId);
}
});
EXPECT_EQ(3, count) << "Missing a restore shouldn't result in an unmatched saveLayer";
}
OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_simpleUnclipped) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
canvas.drawRect(10, 20, 190, 180, SkPaint());
canvas.restore();
});
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
switch (count++) {
case 0:
EXPECT_EQ(RecordedOpId::BeginUnclippedLayerOp, op.opId);
EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
EXPECT_EQ(nullptr, op.localClip);
EXPECT_TRUE(op.localMatrix.isIdentity());
break;
case 1:
EXPECT_EQ(RecordedOpId::RectOp, op.opId);
EXPECT_EQ(nullptr, op.localClip);
EXPECT_EQ(Rect(10, 20, 190, 180), op.unmappedBounds);
EXPECT_TRUE(op.localMatrix.isIdentity());
break;
case 2:
EXPECT_EQ(RecordedOpId::EndUnclippedLayerOp, op.opId);
// Don't bother asserting recording state data - it's not used
break;
default:
ADD_FAILURE();
}
});
EXPECT_EQ(3, count);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_addClipFlag) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(10, 20, 190, 180, SkClipOp::kIntersect);
canvas.saveLayerAlpha(10, 20, 190, 180, 128, (SaveFlags::Flags)0); // unclipped
canvas.drawRect(10, 20, 190, 180, SkPaint());
canvas.restore();
canvas.restore();
});
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
if (count++ == 0) {
EXPECT_EQ(RecordedOpId::BeginLayerOp, op.opId)
<< "Clip + unclipped saveLayer should result in a clipped layer";
}
});
EXPECT_EQ(3, count);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_viewportCrop) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
// shouldn't matter, since saveLayer will clip to its bounds
canvas.clipRect(-1000, -1000, 1000, 1000, SkClipOp::kReplace_deprecated);
canvas.saveLayerAlpha(100, 100, 300, 300, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
});
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
if (count++ == 1) {
Matrix4 expectedMatrix;
EXPECT_EQ(RecordedOpId::RectOp, op.opId);
EXPECT_CLIP_RECT(Rect(100, 100), op.localClip) // Recorded clip rect should be
// intersection of viewport and saveLayer bounds, in layer space;
EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
expectedMatrix.loadTranslate(-100, -100, 0);
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
}
});
EXPECT_EQ(3, count);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateUnclipped) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.translate(100, 100);
canvas.rotate(45);
canvas.translate(-50, -50);
canvas.saveLayerAlpha(0, 0, 100, 100, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 100, 100, SkPaint());
canvas.restore();
canvas.restore();
});
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
if (count++ == 1) {
EXPECT_EQ(RecordedOpId::RectOp, op.opId);
EXPECT_CLIP_RECT(Rect(100, 100), op.localClip);
EXPECT_EQ(Rect(100, 100), op.unmappedBounds);
EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), op.localMatrix)
<< "Recorded op shouldn't see any canvas transform before the saveLayer";
}
});
EXPECT_EQ(3, count);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rotateClipped) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.translate(100, 100);
canvas.rotate(45);
canvas.translate(-200, -200);
// area of saveLayer will be clipped to parent viewport, so we ask for 400x400...
canvas.saveLayerAlpha(0, 0, 400, 400, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.restore();
canvas.restore();
});
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
if (count++ == 1) {
Matrix4 expectedMatrix;
EXPECT_EQ(RecordedOpId::RectOp, op.opId);
// ...and get about 58.6, 58.6, 341.4 341.4, because the bounds are clipped by
// the parent 200x200 viewport, but prior to rotation
ASSERT_NE(nullptr, op.localClip);
ASSERT_EQ(ClipMode::Rectangle, op.localClip->mode);
// NOTE: this check relies on saveLayer altering the clip post-viewport init. This
// causes the clip to be recorded by contained draw commands, though it's not necessary
// since the same clip will be computed at draw time. If such a change is made, this
// check could be done at record time by querying the clip, or the clip could be altered
// slightly so that it is serialized.
EXPECT_EQ(Rect(59, 59, 341, 341), op.localClip->rect);
EXPECT_EQ(Rect(400, 400), op.unmappedBounds);
expectedMatrix.loadIdentity();
EXPECT_MATRIX_APPROX_EQ(expectedMatrix, op.localMatrix);
}
});
EXPECT_EQ(3, count);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, saveLayer_rejectBegin) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.translate(0, -20); // avoid identity case
// empty clip rect should force layer + contents to be rejected
canvas.clipRect(0, -20, 200, -20, SkClipOp::kIntersect);
canvas.saveLayerAlpha(0, 0, 200, 200, 128, SaveFlags::ClipToLayer);
canvas.drawRect(0, 0, 200, 200, SkPaint());
canvas.restore();
canvas.restore();
});
ASSERT_EQ(0u, dl->getOps().size()) << "Begin/Rect/End should all be rejected.";
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_rejection) {
auto child =
TestUtils::createNode(50, 50, 150, 150, [](RenderProperties& props, Canvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
});
auto dl = TestUtils::createDisplayList<RecordingCanvas>(
200, 200, [&child](RecordingCanvas& canvas) {
canvas.clipRect(0, 0, 0, 0, SkClipOp::kIntersect); // empty clip, reject node
canvas.drawRenderNode(child.get()); // shouldn't crash when rejecting node...
});
ASSERT_TRUE(dl->isEmpty());
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawRenderNode_projection) {
sp<RenderNode> background =
TestUtils::createNode(50, 50, 150, 150, [](RenderProperties& props, Canvas& canvas) {
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
});
{
background->mutateStagingProperties().setProjectionReceiver(false);
// NO RECEIVER PRESENT
auto dl = TestUtils::createDisplayList<RecordingCanvas>(
200, 200, [&background](RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 100, 100, SkPaint());
canvas.drawRenderNode(background.get());
canvas.drawRect(0, 0, 100, 100, SkPaint());
});
EXPECT_EQ(-1, dl->projectionReceiveIndex)
<< "no projection receiver should have been observed";
}
{
background->mutateStagingProperties().setProjectionReceiver(true);
// RECEIVER PRESENT
auto dl = TestUtils::createDisplayList<RecordingCanvas>(
200, 200, [&background](RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 100, 100, SkPaint());
canvas.drawRenderNode(background.get());
canvas.drawRect(0, 0, 100, 100, SkPaint());
});
ASSERT_EQ(3u, dl->getOps().size()) << "Must be three ops";
auto op = dl->getOps()[1];
EXPECT_EQ(RecordedOpId::RenderNodeOp, op->opId);
EXPECT_EQ(1, dl->projectionReceiveIndex) << "correct projection receiver not identified";
// verify the behavior works even though projection receiver hasn't been sync'd yet
EXPECT_TRUE(background->stagingProperties().isProjectionReceiver());
EXPECT_FALSE(background->properties().isProjectionReceiver());
}
}
OPENGL_PIPELINE_TEST(RecordingCanvas, firstClipWillReplace) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
// since no explicit clip set on canvas, this should be the one observed on op:
canvas.clipRect(-100, -100, 300, 300, SkClipOp::kIntersect);
SkPaint paint;
paint.setColor(SK_ColorWHITE);
canvas.drawRect(0, 0, 100, 100, paint);
canvas.restore();
});
ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
// first clip must be preserved, even if it extends beyond canvas bounds
EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
canvas.save(SaveFlags::MatrixClip);
canvas.clipRect(-10, -10, 110, 110, SkClipOp::kReplace_deprecated);
canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver);
canvas.restore();
});
ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
// first clip must be preserved, even if it extends beyond canvas bounds
EXPECT_CLIP_RECT(Rect(-10, -10, 110, 110), dl->getOps()[0]->localClip);
EXPECT_TRUE(dl->getOps()[0]->localClip->intersectWithRoot);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.insertReorderBarrier(true);
canvas.insertReorderBarrier(false);
canvas.insertReorderBarrier(false);
canvas.insertReorderBarrier(true);
canvas.drawRect(0, 0, 400, 400, SkPaint());
canvas.insertReorderBarrier(false);
});
auto chunks = dl->getChunks();
EXPECT_EQ(0u, chunks[0].beginOpIndex);
EXPECT_EQ(1u, chunks[0].endOpIndex);
EXPECT_FALSE(chunks[0].reorderChildren);
EXPECT_EQ(1u, chunks[1].beginOpIndex);
EXPECT_EQ(2u, chunks[1].endOpIndex);
EXPECT_TRUE(chunks[1].reorderChildren);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, insertReorderBarrier_clip) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
// first chunk: no recorded clip
canvas.insertReorderBarrier(true);
canvas.drawRect(0, 0, 400, 400, SkPaint());
// second chunk: no recorded clip, since inorder region
canvas.clipRect(0, 0, 200, 200, SkClipOp::kIntersect);
canvas.insertReorderBarrier(false);
canvas.drawRect(0, 0, 400, 400, SkPaint());
// third chunk: recorded clip
canvas.insertReorderBarrier(true);
canvas.drawRect(0, 0, 400, 400, SkPaint());
});
auto chunks = dl->getChunks();
ASSERT_EQ(3u, chunks.size());
EXPECT_TRUE(chunks[0].reorderChildren);
EXPECT_EQ(nullptr, chunks[0].reorderClip);
EXPECT_FALSE(chunks[1].reorderChildren);
EXPECT_EQ(nullptr, chunks[1].reorderClip);
EXPECT_TRUE(chunks[2].reorderChildren);
ASSERT_NE(nullptr, chunks[2].reorderClip);
EXPECT_EQ(Rect(200, 200), chunks[2].reorderClip->rect);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, refPaint) {
SkPaint paint;
auto dl = TestUtils::createDisplayList<RecordingCanvas>(
200, 200, [&paint](RecordingCanvas& canvas) {
paint.setColor(SK_ColorBLUE);
// first two should use same paint
canvas.drawRect(0, 0, 200, 10, paint);
SkPaint paintCopy(paint);
canvas.drawRect(0, 10, 200, 20, paintCopy);
// only here do we use different paint ptr
paint.setColor(SK_ColorRED);
canvas.drawRect(0, 20, 200, 30, paint);
});
auto ops = dl->getOps();
ASSERT_EQ(3u, ops.size());
// first two are the same
EXPECT_NE(nullptr, ops[0]->paint);
EXPECT_NE(&paint, ops[0]->paint);
EXPECT_EQ(ops[0]->paint, ops[1]->paint);
// last is different, but still copied / non-null
EXPECT_NE(nullptr, ops[2]->paint);
EXPECT_NE(ops[0]->paint, ops[2]->paint);
EXPECT_NE(&paint, ops[2]->paint);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmap) {
sk_sp<Bitmap> bitmap(TestUtils::createBitmap(100, 100));
auto dl = TestUtils::createDisplayList<RecordingCanvas>(
100, 100,
[&bitmap](RecordingCanvas& canvas) { canvas.drawBitmap(*bitmap, 0, 0, nullptr); });
auto& bitmaps = dl->getBitmapResources();
EXPECT_EQ(1u, bitmaps.size());
}
OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_bitmapShader) {
sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
auto dl = TestUtils::createDisplayList<RecordingCanvas>(
100, 100, [&bitmap](RecordingCanvas& canvas) {
SkPaint paint;
SkBitmap skBitmap;
bitmap->getSkBitmap(&skBitmap);
sk_sp<SkImage> image =
SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
sk_sp<SkShader> shader =
image->makeShader(SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode, nullptr);
paint.setShader(std::move(shader));
canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
});
auto& bitmaps = dl->getBitmapResources();
EXPECT_EQ(1u, bitmaps.size());
}
OPENGL_PIPELINE_TEST(RecordingCanvas, refBitmapInShader_composeShader) {
sk_sp<Bitmap> bitmap = TestUtils::createBitmap(100, 100);
auto dl = TestUtils::createDisplayList<RecordingCanvas>(
100, 100, [&bitmap](RecordingCanvas& canvas) {
SkPaint paint;
SkBitmap skBitmap;
bitmap->getSkBitmap(&skBitmap);
sk_sp<SkImage> image =
SkMakeImageFromRasterBitmap(skBitmap, kNever_SkCopyPixelsMode);
sk_sp<SkShader> shader1 =
image->makeShader(SkShader::TileMode::kClamp_TileMode,
SkShader::TileMode::kClamp_TileMode, nullptr);
SkPoint center;
center.set(50, 50);
SkColor colors[2];
colors[0] = Color::Black;
colors[1] = Color::White;
sk_sp<SkShader> shader2 = SkGradientShader::MakeRadial(
center, 50, colors, nullptr, 2, SkShader::TileMode::kRepeat_TileMode);
sk_sp<SkShader> composeShader = SkShader::MakeComposeShader(
std::move(shader1), std::move(shader2), SkBlendMode::kMultiply);
paint.setShader(std::move(composeShader));
canvas.drawRoundRect(0, 0, 100, 100, 20.0f, 20.0f, paint);
});
auto& bitmaps = dl->getBitmapResources();
EXPECT_EQ(1u, bitmaps.size());
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawText) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
Paint paint;
paint.setAntiAlias(true);
paint.setTextSize(20);
TestUtils::drawUtf8ToCanvas(&canvas, "HELLO", paint, 25, 25);
});
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
count++;
ASSERT_EQ(RecordedOpId::TextOp, op.opId);
EXPECT_EQ(nullptr, op.localClip);
EXPECT_TRUE(op.localMatrix.isIdentity());
EXPECT_TRUE(op.unmappedBounds.getHeight() >= 10);
EXPECT_TRUE(op.unmappedBounds.getWidth() >= 25);
});
ASSERT_EQ(1, count);
}
OPENGL_PIPELINE_TEST(RecordingCanvas, drawTextInHighContrast) {
Properties::enableHighContrastText = true;
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
Paint paint;
paint.setColor(SK_ColorWHITE);
paint.setAntiAlias(true);
paint.setTextSize(20);
TestUtils::drawUtf8ToCanvas(&canvas, "HELLO", paint, 25, 25);
});
Properties::enableHighContrastText = false;
int count = 0;
playbackOps(*dl, [&count](const RecordedOp& op) {
ASSERT_EQ(RecordedOpId::TextOp, op.opId);
if (count++ == 0) {
EXPECT_EQ(SK_ColorBLACK, op.paint->getColor());
EXPECT_EQ(SkPaint::kStrokeAndFill_Style, op.paint->getStyle());
} else {
EXPECT_EQ(SK_ColorWHITE, op.paint->getColor());
EXPECT_EQ(SkPaint::kFill_Style, op.paint->getStyle());
}
});
ASSERT_EQ(2, count);
}
} // namespace uirenderer
} // namespace android

View File

@@ -26,41 +26,6 @@
using namespace android;
using namespace android::uirenderer;
/**
* Verify that we get the same culling bounds for text for (1) drawing glyphs
* directly to a Canvas or (2) going through a SkPicture as an intermediate step.
*/
OPENGL_PIPELINE_TEST(SkiaCanvasProxy, drawGlyphsViaPicture) {
auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
// setup test variables
SkPaint paint;
paint.setAntiAlias(true);
paint.setTextSize(20);
static const char* text = "testing text bounds";
// draw text directly into Recording canvas
TestUtils::drawUtf8ToCanvas(&canvas, text, paint, 25, 25);
// record the same text draw into a SkPicture and replay it into a Recording canvas
SkPictureRecorder recorder;
SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0);
std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas));
TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25);
sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
canvas.asSkCanvas()->drawPicture(picture);
});
// verify that the text bounds and matrices match
ASSERT_EQ(2U, dl->getOps().size());
auto directOp = dl->getOps()[0];
auto pictureOp = dl->getOps()[1];
ASSERT_EQ(RecordedOpId::TextOp, directOp->opId);
EXPECT_EQ(directOp->opId, pictureOp->opId);
EXPECT_EQ(directOp->unmappedBounds, pictureOp->unmappedBounds);
EXPECT_EQ(directOp->localMatrix, pictureOp->localMatrix);
}
TEST(SkiaCanvas, drawShadowLayer) {
auto surface = SkSurface::MakeRasterN32Premul(10, 10);
SkiaCanvas canvas(surface->getCanvas());

View File

@@ -1,56 +0,0 @@
/*
* Copyright (C) 2016 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 "GammaFontRenderer.h"
#include "TextDropShadowCache.h"
#include "tests/common/TestUtils.h"
#include "utils/Blur.h"
#include <SkPaint.h>
using namespace android;
using namespace android::uirenderer;
RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) {
SkPaint paint;
paint.setTextSize(20);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
GammaFontRenderer gammaFontRenderer;
FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
fontRenderer.setFont(&paint, SkMatrix::I());
TextDropShadowCache cache(MB(5));
cache.setFontRenderer(fontRenderer);
std::vector<glyph_t> glyphs;
std::vector<float> positions;
float totalAdvance;
uirenderer::Rect bounds;
TestUtils::layoutTextUnscaled(paint, "This is a test", &glyphs, &positions, &totalAdvance,
&bounds);
EXPECT_TRUE(bounds.contains(5, -10, 100, 0)) << "Expect input to be nontrivially sized";
ShadowTexture* texture = cache.get(&paint, glyphs.data(), glyphs.size(), 10, positions.data());
ASSERT_TRUE(texture);
ASSERT_FALSE(texture->cleanup);
ASSERT_EQ((uint32_t)texture->objectSize(), cache.getSize());
ASSERT_TRUE(cache.getSize());
cache.clear();
ASSERT_EQ(cache.getSize(), 0u);
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright (C) 2017 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 "Extensions.h"
#include "TextureCache.h"
#include "tests/common/TestUtils.h"
using namespace android;
using namespace android::uirenderer;
RENDERTHREAD_OPENGL_PIPELINE_TEST(TextureCache, clear) {
TextureCache cache;
ASSERT_EQ(cache.getSize(), 0u);
// it is not 0, because FontRenderer allocates one texture
int initialCount = GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture);
SkBitmap skBitmap;
SkImageInfo info = SkImageInfo::Make(100, 100, kN32_SkColorType, kPremul_SkAlphaType);
skBitmap.setInfo(info);
sk_sp<Bitmap> hwBitmap(renderThread.allocateHardwareBitmap(skBitmap));
cache.get(hwBitmap.get());
ASSERT_EQ(GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture), initialCount + 1);
cache.clear();
ASSERT_EQ(GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture), initialCount);
}

View File

@@ -21,7 +21,6 @@
#include "draw_gl.h"
#include <Properties.h>
#include <errno.h>
#include <jni.h>
#include <private/hwui/DrawGlInfo.h>
@@ -54,13 +53,7 @@ class DrawGLFunctor : public Functor {
}
AwDrawGLInfo aw_info;
// TODO(boliu): Remove property check once OpenGL fallback is removed.
auto render_pipeline_type =
android::uirenderer::Properties::getRenderPipelineType();
aw_info.version = (render_pipeline_type ==
android::uirenderer::RenderPipelineType::OpenGL)
? 2
: kAwDrawGLInfoVersion;
aw_info.version = kAwDrawGLInfoVersion;
switch (what) {
case DrawGlInfo::kModeDraw: {
aw_info.mode = AwDrawGLInfo::kModeDraw;