Stan Iliev | 021693b | 2016-10-17 16:26:15 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include <gtest/gtest.h> |
| 18 | #include <VectorDrawable.h> |
| 19 | |
| 20 | #include "AnimationContext.h" |
| 21 | #include "DamageAccumulator.h" |
| 22 | #include "IContextFactory.h" |
| 23 | #include "pipeline/skia/SkiaDisplayList.h" |
| 24 | #include "pipeline/skia/SkiaRecordingCanvas.h" |
| 25 | #include "renderthread/CanvasContext.h" |
| 26 | #include "tests/common/TestUtils.h" |
| 27 | #include "SkiaCanvas.h" |
| 28 | #include <SkLiteRecorder.h> |
| 29 | #include <string.h> |
| 30 | |
| 31 | |
| 32 | using namespace android; |
| 33 | using namespace android::uirenderer; |
| 34 | using namespace android::uirenderer::renderthread; |
| 35 | using namespace android::uirenderer::skiapipeline; |
| 36 | |
| 37 | static sp<RenderNode> createSkiaNode(int left, int top, int right, int bottom, |
| 38 | std::function<void(RenderProperties& props, SkiaRecordingCanvas& canvas)> setup, |
| 39 | const char* name = nullptr, SkiaDisplayList* displayList = nullptr) { |
| 40 | #if HWUI_NULL_GPU |
| 41 | // if RenderNodes are being sync'd/used, device info will be needed, since |
| 42 | // DeviceInfo::maxTextureSize() affects layer property |
| 43 | DeviceInfo::initialize(); |
| 44 | #endif |
| 45 | sp<RenderNode> node = new RenderNode(); |
| 46 | if (name) { |
| 47 | node->setName(name); |
| 48 | } |
| 49 | RenderProperties& props = node->mutateStagingProperties(); |
| 50 | props.setLeftTopRightBottom(left, top, right, bottom); |
| 51 | if (displayList) { |
| 52 | node->setStagingDisplayList(displayList, nullptr); |
| 53 | } |
| 54 | if (setup) { |
| 55 | std::unique_ptr<SkiaRecordingCanvas> canvas(new SkiaRecordingCanvas(nullptr, |
| 56 | props.getWidth(), props.getHeight())); |
| 57 | setup(props, *canvas.get()); |
| 58 | node->setStagingDisplayList(canvas->finishRecording(), nullptr); |
| 59 | } |
| 60 | node->setPropertyFieldsDirty(0xFFFFFFFF); |
| 61 | TestUtils::syncHierarchyPropertiesAndDisplayList(node); |
| 62 | return node; |
| 63 | } |
| 64 | |
| 65 | TEST(RenderNodeDrawable, create) { |
| 66 | auto rootNode = TestUtils::createNode(0, 0, 200, 400, |
| 67 | [](RenderProperties& props, Canvas& canvas) { |
| 68 | canvas.drawColor(Color::Red_500, SkBlendMode::kSrcOver); |
| 69 | }); |
| 70 | |
| 71 | auto skLiteDL = SkLiteDL::New(SkRect::MakeWH(1, 1)); |
| 72 | SkLiteRecorder canvas; |
| 73 | canvas.reset(skLiteDL.get()); |
| 74 | canvas.translate(100, 100); |
| 75 | RenderNodeDrawable drawable(rootNode.get(), &canvas); |
| 76 | |
| 77 | ASSERT_EQ(drawable.getRenderNode(), rootNode.get()); |
| 78 | ASSERT_EQ(&drawable.getNodeProperties(), &rootNode->properties()); |
| 79 | ASSERT_EQ(drawable.getRecordedMatrix(), canvas.getTotalMatrix()); |
| 80 | } |
| 81 | |
| 82 | TEST(RenderNodeDrawable, drawContent) { |
| 83 | auto surface = SkSurface::MakeRasterN32Premul(1, 1); |
| 84 | SkCanvas& canvas = *surface->getCanvas(); |
| 85 | canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); |
| 86 | ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); |
| 87 | |
| 88 | //create a RenderNodeDrawable backed by a RenderNode backed by a SkLiteRecorder |
| 89 | auto rootNode = createSkiaNode(0, 0, 1, 1, |
| 90 | [](RenderProperties& props, SkiaRecordingCanvas& recorder) { |
| 91 | recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); |
| 92 | }); |
| 93 | RenderNodeDrawable drawable(rootNode.get(), &canvas, false); |
| 94 | |
| 95 | //negative and positive Z order are drawn out of order |
| 96 | rootNode->animatorProperties().setElevation(10.0f); |
| 97 | canvas.drawDrawable(&drawable); |
| 98 | ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); |
| 99 | rootNode->animatorProperties().setElevation(-10.0f); |
| 100 | canvas.drawDrawable(&drawable); |
| 101 | ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); |
| 102 | |
| 103 | //zero Z are drawn immediately |
| 104 | rootNode->animatorProperties().setElevation(0.0f); |
| 105 | canvas.drawDrawable(&drawable); |
| 106 | ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); |
| 107 | } |
| 108 | |
| 109 | //TODO: another test that verifies equal z values are drawn in order, and barriers prevent Z |
| 110 | //intermixing (model after FrameBuilder zReorder) |
| 111 | TEST(RenderNodeDrawable, drawAndReorder) { |
| 112 | //this test exercises StartReorderBarrierDrawable, EndReorderBarrierDrawable and |
| 113 | //SkiaRecordingCanvas |
| 114 | auto surface = SkSurface::MakeRasterN32Premul(4, 4); |
| 115 | SkCanvas& canvas = *surface->getCanvas(); |
| 116 | |
| 117 | canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); |
| 118 | ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE); |
| 119 | |
| 120 | //-z draws to all 4 pixels (RED) |
| 121 | auto redNode = createSkiaNode(0, 0, 4, 4, |
| 122 | [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { |
| 123 | redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); |
| 124 | props.setElevation(-10.0f); |
| 125 | }, "redNode"); |
| 126 | |
| 127 | //0z draws to bottom 2 pixels (GREEN) |
| 128 | auto bottomHalfGreenNode = createSkiaNode(0, 0, 4, 4, |
| 129 | [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) { |
| 130 | SkPaint greenPaint; |
| 131 | greenPaint.setColor(SK_ColorGREEN); |
| 132 | greenPaint.setStyle(SkPaint::kFill_Style); |
| 133 | bottomHalfGreenCanvas.drawRect(0, 2, 4, 4, greenPaint); |
| 134 | props.setElevation(0.0f); |
| 135 | }, "bottomHalfGreenNode"); |
| 136 | |
| 137 | //+z draws to right 2 pixels (BLUE) |
| 138 | auto rightHalfBlueNode = createSkiaNode(0, 0, 4, 4, |
| 139 | [](RenderProperties& props, SkiaRecordingCanvas& rightHalfBlueCanvas) { |
| 140 | SkPaint bluePaint; |
| 141 | bluePaint.setColor(SK_ColorBLUE); |
| 142 | bluePaint.setStyle(SkPaint::kFill_Style); |
| 143 | rightHalfBlueCanvas.drawRect(2, 0, 4, 4, bluePaint); |
| 144 | props.setElevation(10.0f); |
| 145 | }, "rightHalfBlueNode"); |
| 146 | |
| 147 | auto rootNode = createSkiaNode(0, 0, 4, 4, |
| 148 | [&](RenderProperties& props, SkiaRecordingCanvas& rootRecorder) { |
| 149 | rootRecorder.insertReorderBarrier(true); |
| 150 | //draw in reverse Z order, so Z alters draw order |
| 151 | rootRecorder.drawRenderNode(rightHalfBlueNode.get()); |
| 152 | rootRecorder.drawRenderNode(bottomHalfGreenNode.get()); |
| 153 | rootRecorder.drawRenderNode(redNode.get()); |
| 154 | }, "rootNode"); |
| 155 | |
| 156 | RenderNodeDrawable drawable3(rootNode.get(), &canvas, false); |
| 157 | canvas.drawDrawable(&drawable3); |
| 158 | ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); |
| 159 | ASSERT_EQ(TestUtils::getColor(surface, 0, 3), SK_ColorGREEN); |
| 160 | ASSERT_EQ(TestUtils::getColor(surface, 3, 3), SK_ColorBLUE); |
| 161 | } |
| 162 | |
| 163 | TEST(RenderNodeDrawable, composeOnLayer) |
| 164 | { |
| 165 | auto surface = SkSurface::MakeRasterN32Premul(1, 1); |
| 166 | SkCanvas& canvas = *surface->getCanvas(); |
| 167 | canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); |
| 168 | ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); |
| 169 | |
| 170 | auto rootNode = createSkiaNode(0, 0, 1, 1, |
| 171 | [](RenderProperties& props, SkiaRecordingCanvas& recorder) { |
| 172 | recorder.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); |
| 173 | }); |
| 174 | |
| 175 | //attach a layer to the render node |
| 176 | auto surfaceLayer = SkSurface::MakeRasterN32Premul(1, 1); |
| 177 | auto canvas2 = surfaceLayer->getCanvas(); |
| 178 | canvas2->drawColor(SK_ColorWHITE, SkBlendMode::kSrcOver); |
| 179 | rootNode->setLayerSurface( surfaceLayer ); |
| 180 | |
| 181 | RenderNodeDrawable drawable1(rootNode.get(), &canvas, false); |
| 182 | canvas.drawDrawable(&drawable1); |
| 183 | ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0)); |
| 184 | |
| 185 | RenderNodeDrawable drawable2(rootNode.get(), &canvas, true); |
| 186 | canvas.drawDrawable(&drawable2); |
| 187 | ASSERT_EQ(SK_ColorWHITE, TestUtils::getColor(surface, 0, 0)); |
| 188 | |
| 189 | RenderNodeDrawable drawable3(rootNode.get(), &canvas, false); |
| 190 | canvas.drawDrawable(&drawable3); |
| 191 | ASSERT_EQ(SK_ColorRED, TestUtils::getColor(surface, 0, 0)); |
| 192 | |
| 193 | rootNode->setLayerSurface( sk_sp<SkSurface>() ); |
| 194 | } |
| 195 | |
| 196 | //TODO: refactor to cover test cases from FrameBuilderTests_projectionReorder |
| 197 | //validate with bounds and projection path mask. |
| 198 | //TODO: research if we could hook in and mock/validate different aspects of the drawing, |
| 199 | //instead of validating pixels |
| 200 | TEST(RenderNodeDrawable, projectDraw) { |
| 201 | auto surface = SkSurface::MakeRasterN32Premul(1, 1); |
| 202 | SkCanvas& canvas = *surface->getCanvas(); |
| 203 | canvas.drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); |
| 204 | ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); |
| 205 | |
| 206 | auto redNode = createSkiaNode(0, 0, 1, 1, |
| 207 | [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) { |
| 208 | redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver); |
| 209 | }, "redNode"); |
| 210 | |
| 211 | auto greenNodeWithRedChild = createSkiaNode(0, 0, 1, 1, |
| 212 | [&](RenderProperties& props, SkiaRecordingCanvas& greenCanvasWithRedChild) { |
| 213 | greenCanvasWithRedChild.drawRenderNode(redNode.get()); |
| 214 | greenCanvasWithRedChild.drawColor(SK_ColorGREEN, SkBlendMode::kSrcOver); |
| 215 | }, "greenNodeWithRedChild"); |
| 216 | |
| 217 | auto rootNode = createSkiaNode(0, 0, 1, 1, |
| 218 | [&](RenderProperties& props, SkiaRecordingCanvas& rootCanvas) { |
| 219 | rootCanvas.drawRenderNode(greenNodeWithRedChild.get()); |
| 220 | }, "rootNode"); |
| 221 | SkiaDisplayList* rootDisplayList = static_cast<SkiaDisplayList*>( |
| 222 | (const_cast<DisplayList*>(rootNode->getDisplayList()))); |
| 223 | |
| 224 | RenderNodeDrawable rootDrawable(rootNode.get(), &canvas, false); |
| 225 | canvas.drawDrawable(&rootDrawable); |
| 226 | ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorGREEN); |
| 227 | |
| 228 | //project redNode on rootNode, which will change the test outcome, |
| 229 | //because redNode will draw after greenNodeWithRedChild |
| 230 | rootDisplayList->mIsProjectionReceiver = true; |
| 231 | redNode->animatorProperties().setProjectBackwards(true); |
| 232 | canvas.drawDrawable(&rootDrawable); |
| 233 | ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); |
| 234 | } |