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:
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
@@ -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
|
||||
@@ -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";
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user