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