1 /* 2 * Copyright (C) 2015 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 #pragma once 18 19 #include <DisplayList.h> 20 #include <Matrix.h> 21 #include <Properties.h> 22 #include <Rect.h> 23 #include <RenderNode.h> 24 #include <hwui/Bitmap.h> 25 #include <pipeline/skia/SkiaRecordingCanvas.h> 26 #include <private/hwui/DrawGlInfo.h> 27 #include <renderstate/RenderState.h> 28 #include <renderthread/RenderThread.h> 29 30 #include <gtest/gtest.h> 31 #include <memory> 32 #include <unordered_map> 33 34 namespace android { 35 namespace uirenderer { 36 37 #define EXPECT_MATRIX_APPROX_EQ(a, b) EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b)) 38 39 #define EXPECT_RECT_APPROX_EQ(a, b) \ 40 EXPECT_TRUE(MathUtils::areEqual((a).left, (b).left) && \ 41 MathUtils::areEqual((a).top, (b).top) && \ 42 MathUtils::areEqual((a).right, (b).right) && \ 43 MathUtils::areEqual((a).bottom, (b).bottom)); 44 45 #define EXPECT_CLIP_RECT(expRect, clipStatePtr) \ 46 EXPECT_NE(nullptr, (clipStatePtr)) << "Op is unclipped"; \ 47 if ((clipStatePtr)->mode == ClipMode::Rectangle) { \ 48 EXPECT_EQ((expRect), reinterpret_cast<const ClipRect*>(clipStatePtr)->rect); \ 49 } else { \ 50 ADD_FAILURE() << "ClipState not a rect"; \ 51 } 52 53 #define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \ 54 TEST(test_case_name, test_name##_##pipeline) { \ 55 RenderPipelineType oldType = Properties::getRenderPipelineType(); \ 56 Properties::overrideRenderPipelineType(RenderPipelineType::pipeline, true); \ 57 functionCall; \ 58 Properties::overrideRenderPipelineType(oldType, true); \ 59 }; 60 61 #define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \ 62 INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \ 63 TestUtils::runOnRenderThread( \ 64 test_case_name##_##test_name##_RenderThreadTest::doTheThing)) 65 66 /** 67 * Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope 68 * (for e.g. accessing its RenderState) 69 */ 70 #define RENDERTHREAD_TEST(test_case_name, test_name) \ 71 class test_case_name##_##test_name##_RenderThreadTest { \ 72 public: \ 73 static void doTheThing(renderthread::RenderThread& renderThread); \ 74 }; \ 75 INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \ 76 INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); \ 77 void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \ 78 renderthread::RenderThread& renderThread) 79 80 /** 81 * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes 82 */ 83 #define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \ 84 class test_case_name##_##test_name##_RenderThreadTest { \ 85 public: \ 86 static void doTheThing(renderthread::RenderThread& renderThread); \ 87 }; \ 88 INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \ 89 INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); \ 90 void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \ 91 renderthread::RenderThread& renderThread) 92 93 /** 94 * Sets a property value temporarily, generally for the duration of a test, restoring the previous 95 * value when going out of scope. 96 * 97 * Can be used e.g. to test behavior only active while Properties::debugOverdraw is enabled. 98 */ 99 template <typename T> 100 class ScopedProperty { 101 public: ScopedProperty(T & property,T newValue)102 ScopedProperty(T& property, T newValue) : mPropertyPtr(&property), mOldValue(property) { 103 property = newValue; 104 } ~ScopedProperty()105 ~ScopedProperty() { *mPropertyPtr = mOldValue; } 106 107 private: 108 T* mPropertyPtr; 109 T mOldValue; 110 }; 111 112 class TestUtils { 113 public: 114 class SignalingDtor { 115 public: SignalingDtor()116 SignalingDtor() : mSignal(nullptr) {} SignalingDtor(int * signal)117 explicit SignalingDtor(int* signal) : mSignal(signal) {} setSignal(int * signal)118 void setSignal(int* signal) { mSignal = signal; } ~SignalingDtor()119 ~SignalingDtor() { 120 if (mSignal) { 121 (*mSignal)++; 122 } 123 } 124 125 private: 126 int* mSignal; 127 }; 128 129 class MockTreeObserver : public TreeObserver { 130 public: onMaybeRemovedFromTree(RenderNode * node)131 virtual void onMaybeRemovedFromTree(RenderNode* node) {} 132 }; 133 matricesAreApproxEqual(const Matrix4 & a,const Matrix4 & b)134 static bool matricesAreApproxEqual(const Matrix4& a, const Matrix4& b) { 135 for (int i = 0; i < 16; i++) { 136 if (!MathUtils::areEqual(a[i], b[i])) { 137 return false; 138 } 139 } 140 return true; 141 } 142 143 static sk_sp<Bitmap> createBitmap(int width, int height, 144 SkColorType colorType = kN32_SkColorType) { 145 SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType); 146 return Bitmap::allocateHeapBitmap(info); 147 } 148 createBitmap(int width,int height,SkBitmap * outBitmap)149 static sk_sp<Bitmap> createBitmap(int width, int height, SkBitmap* outBitmap) { 150 SkImageInfo info = SkImageInfo::Make(width, height, kN32_SkColorType, kPremul_SkAlphaType); 151 outBitmap->setInfo(info); 152 return Bitmap::allocateHeapBitmap(outBitmap); 153 } 154 155 static sp<DeferredLayerUpdater> createTextureLayerUpdater( 156 renderthread::RenderThread& renderThread); 157 158 static sp<DeferredLayerUpdater> createTextureLayerUpdater( 159 renderthread::RenderThread& renderThread, uint32_t width, uint32_t height, 160 const SkMatrix& transform); 161 createNode(int left,int top,int right,int bottom,std::function<void (RenderProperties & props,Canvas & canvas)> setup)162 static sp<RenderNode> createNode( 163 int left, int top, int right, int bottom, 164 std::function<void(RenderProperties& props, Canvas& canvas)> setup) { 165 sp<RenderNode> node = new RenderNode(); 166 RenderProperties& props = node->mutateStagingProperties(); 167 props.setLeftTopRightBottom(left, top, right, bottom); 168 if (setup) { 169 std::unique_ptr<Canvas> canvas( 170 Canvas::create_recording_canvas(props.getWidth(), props.getHeight())); 171 setup(props, *canvas.get()); 172 canvas->finishRecording(node.get()); 173 } 174 node->setPropertyFieldsDirty(0xFFFFFFFF); 175 return node; 176 } 177 178 template <class RecordingCanvasType> createNode(int left,int top,int right,int bottom,std::function<void (RenderProperties & props,RecordingCanvasType & canvas)> setup)179 static sp<RenderNode> createNode( 180 int left, int top, int right, int bottom, 181 std::function<void(RenderProperties& props, RecordingCanvasType& canvas)> setup) { 182 sp<RenderNode> node = new RenderNode(); 183 RenderProperties& props = node->mutateStagingProperties(); 184 props.setLeftTopRightBottom(left, top, right, bottom); 185 if (setup) { 186 RecordingCanvasType canvas(props.getWidth(), props.getHeight()); 187 setup(props, canvas); 188 node->setStagingDisplayList(canvas.finishRecording()); 189 } 190 node->setPropertyFieldsDirty(0xFFFFFFFF); 191 return node; 192 } 193 recordNode(RenderNode & node,std::function<void (Canvas &)> contentCallback)194 static void recordNode(RenderNode& node, std::function<void(Canvas&)> contentCallback) { 195 std::unique_ptr<Canvas> canvas(Canvas::create_recording_canvas( 196 node.stagingProperties().getWidth(), node.stagingProperties().getHeight(), &node)); 197 contentCallback(*canvas.get()); 198 canvas->finishRecording(&node); 199 } 200 201 static sp<RenderNode> createSkiaNode( 202 int left, int top, int right, int bottom, 203 std::function<void(RenderProperties& props, skiapipeline::SkiaRecordingCanvas& canvas)> 204 setup, 205 const char* name = nullptr, 206 std::unique_ptr<skiapipeline::SkiaDisplayList> displayList = nullptr) { 207 sp<RenderNode> node = new RenderNode(); 208 if (name) { 209 node->setName(name); 210 } 211 RenderProperties& props = node->mutateStagingProperties(); 212 props.setLeftTopRightBottom(left, top, right, bottom); 213 if (displayList) { 214 node->setStagingDisplayList(DisplayList(std::move(displayList))); 215 } 216 if (setup) { 217 std::unique_ptr<skiapipeline::SkiaRecordingCanvas> canvas( 218 new skiapipeline::SkiaRecordingCanvas(nullptr, props.getWidth(), 219 props.getHeight())); 220 setup(props, *canvas.get()); 221 canvas->finishRecording(node.get()); 222 } 223 node->setPropertyFieldsDirty(0xFFFFFFFF); 224 TestUtils::syncHierarchyPropertiesAndDisplayList(node); 225 return node; 226 } 227 228 /** 229 * Forces a sync of a tree of RenderNode, such that every descendant will have its staging 230 * properties and DisplayList moved to the render copies. 231 * 232 * Note: does not check dirtiness bits, so any non-staging DisplayLists will be discarded. 233 * For this reason, this should generally only be called once on a tree. 234 */ syncHierarchyPropertiesAndDisplayList(sp<RenderNode> & node)235 static void syncHierarchyPropertiesAndDisplayList(sp<RenderNode>& node) { 236 syncHierarchyPropertiesAndDisplayListImpl(node.get()); 237 } 238 getSyncedNode(sp<RenderNode> & node)239 static sp<RenderNode>& getSyncedNode(sp<RenderNode>& node) { 240 syncHierarchyPropertiesAndDisplayList(node); 241 return node; 242 } 243 244 typedef std::function<void(renderthread::RenderThread& thread)> RtCallback; 245 246 class TestTask : public renderthread::RenderTask { 247 public: TestTask(RtCallback rtCallback)248 explicit TestTask(RtCallback rtCallback) : rtCallback(rtCallback) {} ~TestTask()249 virtual ~TestTask() {} 250 virtual void run() override; 251 RtCallback rtCallback; 252 }; 253 254 /** 255 * NOTE: requires surfaceflinger to run, otherwise this method will wait indefinitely. 256 */ runOnRenderThread(RtCallback rtCallback)257 static void runOnRenderThread(RtCallback rtCallback) { 258 TestTask task(rtCallback); 259 renderthread::RenderThread::getInstance().queue().runSync([&]() { task.run(); }); 260 } 261 runOnRenderThreadUnmanaged(RtCallback rtCallback)262 static void runOnRenderThreadUnmanaged(RtCallback rtCallback) { 263 auto& rt = renderthread::RenderThread::getInstance(); 264 rt.queue().runSync([&]() { rtCallback(rt); }); 265 } 266 267 isRenderThreadRunning()268 static bool isRenderThreadRunning() { return renderthread::RenderThread::hasInstance(); } getRenderThreadTid()269 static pid_t getRenderThreadTid() { return renderthread::RenderThread::getInstance().getTid(); } 270 271 static SkColor interpolateColor(float fraction, SkColor start, SkColor end); 272 273 static void drawUtf8ToCanvas(Canvas* canvas, const char* text, const Paint& paint, float x, 274 float y); 275 276 static void drawUtf8ToCanvas(Canvas* canvas, const char* text, const Paint& paint, 277 const SkPath& path); 278 279 static std::unique_ptr<uint16_t[]> asciiToUtf16(const char* str); 280 281 static SkColor getColor(const sk_sp<SkSurface>& surface, int x, int y); 282 283 static SkRect getClipBounds(const SkCanvas* canvas); 284 static SkRect getLocalClipBounds(const SkCanvas* canvas); 285 286 struct CallCounts { 287 int sync = 0; 288 int contextDestroyed = 0; 289 int destroyed = 0; 290 int removeOverlays = 0; 291 int glesDraw = 0; 292 }; 293 294 static void expectOnRenderThread(const std::string_view& function = "unknown") { 295 EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()) << "Called on wrong thread: " << function; 296 } 297 createMockFunctor(RenderMode mode)298 static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) { 299 auto callbacks = WebViewFunctorCallbacks{ 300 .onSync = 301 [](int functor, void* client_data, const WebViewSyncData& data) { 302 expectOnRenderThread("onSync"); 303 sMockFunctorCounts[functor].sync++; 304 }, 305 .onContextDestroyed = 306 [](int functor, void* client_data) { 307 expectOnRenderThread("onContextDestroyed"); 308 sMockFunctorCounts[functor].contextDestroyed++; 309 }, 310 .onDestroyed = 311 [](int functor, void* client_data) { 312 expectOnRenderThread("onDestroyed"); 313 sMockFunctorCounts[functor].destroyed++; 314 }, 315 .removeOverlays = 316 [](int functor, void* data, 317 void (*mergeTransaction)(ASurfaceTransaction*)) { 318 expectOnRenderThread("removeOverlays"); 319 sMockFunctorCounts[functor].removeOverlays++; 320 }, 321 }; 322 switch (mode) { 323 case RenderMode::OpenGL_ES: 324 callbacks.gles.draw = [](int functor, void* client_data, const DrawGlInfo& params, 325 const WebViewOverlayData& overlay_params) { 326 expectOnRenderThread("draw"); 327 sMockFunctorCounts[functor].glesDraw++; 328 }; 329 break; 330 default: 331 ADD_FAILURE(); 332 return WebViewFunctorCallbacks{}; 333 } 334 return callbacks; 335 } 336 countsForFunctor(int functor)337 static CallCounts& countsForFunctor(int functor) { return sMockFunctorCounts[functor]; } 338 339 private: 340 static std::unordered_map<int, CallCounts> sMockFunctorCounts; 341 syncHierarchyPropertiesAndDisplayListImpl(RenderNode * node)342 static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) { 343 MarkAndSweepRemoved observer(nullptr); 344 node->syncProperties(); 345 if (node->mNeedsDisplayListSync) { 346 node->mNeedsDisplayListSync = false; 347 node->syncDisplayList(observer, nullptr); 348 } 349 auto& displayList = node->getDisplayList(); 350 if (displayList) { 351 displayList.updateChildren([](RenderNode* child) { 352 syncHierarchyPropertiesAndDisplayListImpl(child); 353 }); 354 } 355 } 356 357 }; // class TestUtils 358 359 } /* namespace uirenderer */ 360 } /* namespace android */ 361