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