| // Copyright 2013 The Flutter Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "flutter/flow/skia_gpu_object.h" | 
 |  | 
 | #include <future> | 
 |  | 
 | #include "flutter/fml/message_loop.h" | 
 | #include "flutter/fml/synchronization/waitable_event.h" | 
 | #include "flutter/fml/task_runner.h" | 
 | #include "flutter/testing/thread_test.h" | 
 | #include "gtest/gtest.h" | 
 | #include "third_party/skia/include/core/SkRefCnt.h" | 
 |  | 
 | namespace flutter { | 
 | namespace testing { | 
 |  | 
 | class TestSkObject : public SkRefCnt { | 
 |  public: | 
 |   TestSkObject(std::shared_ptr<fml::AutoResetWaitableEvent> latch, | 
 |                fml::TaskQueueId* dtor_task_queue_id) | 
 |       : latch_(latch), dtor_task_queue_id_(dtor_task_queue_id) {} | 
 |  | 
 |   virtual ~TestSkObject() { | 
 |     if (dtor_task_queue_id_) { | 
 |       *dtor_task_queue_id_ = fml::MessageLoop::GetCurrentTaskQueueId(); | 
 |     } | 
 |     latch_->Signal(); | 
 |   } | 
 |  | 
 |  private: | 
 |   std::shared_ptr<fml::AutoResetWaitableEvent> latch_; | 
 |   fml::TaskQueueId* dtor_task_queue_id_; | 
 | }; | 
 |  | 
 | class TestResourceContext : public TestSkObject { | 
 |  public: | 
 |   TestResourceContext(std::shared_ptr<fml::AutoResetWaitableEvent> latch, | 
 |                       fml::TaskQueueId* dtor_task_queue_id) | 
 |       : TestSkObject(latch, dtor_task_queue_id) {} | 
 |   ~TestResourceContext() = default; | 
 |   void performDeferredCleanup(std::chrono::milliseconds msNotUsed) {} | 
 |   void deleteBackendTexture(GrBackendTexture texture) {} | 
 | }; | 
 |  | 
 | class SkiaGpuObjectTest : public ThreadTest { | 
 |  public: | 
 |   SkiaGpuObjectTest() | 
 |       : unref_task_runner_(CreateNewThread()), | 
 |         unref_queue_(fml::MakeRefCounted<SkiaUnrefQueue>( | 
 |             unref_task_runner(), | 
 |             fml::TimeDelta::FromSeconds(0))), | 
 |         delayed_unref_queue_(fml::MakeRefCounted<SkiaUnrefQueue>( | 
 |             unref_task_runner(), | 
 |             fml::TimeDelta::FromSeconds(3))) { | 
 |     // The unref queues must be created in the same thread of the | 
 |     // unref_task_runner so the queue can access the same-thread-only WeakPtr of | 
 |     // the GrContext constructed during the creation. | 
 |     std::promise<bool> queuesCreated; | 
 |     unref_task_runner_->PostTask([this, &queuesCreated]() { | 
 |       unref_queue_ = fml::MakeRefCounted<SkiaUnrefQueue>( | 
 |           unref_task_runner(), fml::TimeDelta::FromSeconds(0)); | 
 |       delayed_unref_queue_ = fml::MakeRefCounted<SkiaUnrefQueue>( | 
 |           unref_task_runner(), fml::TimeDelta::FromSeconds(3)); | 
 |       queuesCreated.set_value(true); | 
 |     }); | 
 |     queuesCreated.get_future().wait(); | 
 |     ::testing::FLAGS_gtest_death_test_style = "threadsafe"; | 
 |   } | 
 |  | 
 |   fml::RefPtr<fml::TaskRunner> unref_task_runner() { | 
 |     return unref_task_runner_; | 
 |   } | 
 |   fml::RefPtr<SkiaUnrefQueue> unref_queue() { return unref_queue_; } | 
 |   fml::RefPtr<SkiaUnrefQueue> delayed_unref_queue() { | 
 |     return delayed_unref_queue_; | 
 |   } | 
 |  | 
 |  private: | 
 |   fml::RefPtr<fml::TaskRunner> unref_task_runner_; | 
 |   fml::RefPtr<SkiaUnrefQueue> unref_queue_; | 
 |   fml::RefPtr<SkiaUnrefQueue> delayed_unref_queue_; | 
 | }; | 
 |  | 
 | TEST_F(SkiaGpuObjectTest, QueueSimple) { | 
 |   std::shared_ptr<fml::AutoResetWaitableEvent> latch = | 
 |       std::make_shared<fml::AutoResetWaitableEvent>(); | 
 |   fml::TaskQueueId dtor_task_queue_id(0); | 
 |   SkRefCnt* ref_object = new TestSkObject(latch, &dtor_task_queue_id); | 
 |  | 
 |   unref_queue()->Unref(ref_object); | 
 |   latch->Wait(); | 
 |   ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); | 
 | } | 
 |  | 
 | TEST_F(SkiaGpuObjectTest, ObjectDestructor) { | 
 |   std::shared_ptr<fml::AutoResetWaitableEvent> latch = | 
 |       std::make_shared<fml::AutoResetWaitableEvent>(); | 
 |   fml::TaskQueueId dtor_task_queue_id(0); | 
 |   auto object = sk_make_sp<TestSkObject>(latch, &dtor_task_queue_id); | 
 |   { | 
 |     SkiaGPUObject<TestSkObject> sk_object(std::move(object), unref_queue()); | 
 |     // Verify that the default SkiaGPUObject dtor queues and unref. | 
 |   } | 
 |  | 
 |   latch->Wait(); | 
 |   ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); | 
 | } | 
 |  | 
 | TEST_F(SkiaGpuObjectTest, ObjectReset) { | 
 |   std::shared_ptr<fml::AutoResetWaitableEvent> latch = | 
 |       std::make_shared<fml::AutoResetWaitableEvent>(); | 
 |   fml::TaskQueueId dtor_task_queue_id(0); | 
 |   SkiaGPUObject<TestSkObject> sk_object( | 
 |       sk_make_sp<TestSkObject>(latch, &dtor_task_queue_id), unref_queue()); | 
 |   // Verify that explicitly resetting the GPU object queues and unref. | 
 |   sk_object.reset(); | 
 |   ASSERT_EQ(sk_object.skia_object(), nullptr); | 
 |   latch->Wait(); | 
 |   ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); | 
 | } | 
 |  | 
 | TEST_F(SkiaGpuObjectTest, ObjectResetTwice) { | 
 |   std::shared_ptr<fml::AutoResetWaitableEvent> latch = | 
 |       std::make_shared<fml::AutoResetWaitableEvent>(); | 
 |   fml::TaskQueueId dtor_task_queue_id(0); | 
 |   SkiaGPUObject<TestSkObject> sk_object( | 
 |       sk_make_sp<TestSkObject>(latch, &dtor_task_queue_id), unref_queue()); | 
 |  | 
 |   sk_object.reset(); | 
 |   ASSERT_EQ(sk_object.skia_object(), nullptr); | 
 |   sk_object.reset(); | 
 |   ASSERT_EQ(sk_object.skia_object(), nullptr); | 
 |  | 
 |   latch->Wait(); | 
 |   ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); | 
 | } | 
 |  | 
 | TEST_F(SkiaGpuObjectTest, UnrefResourceContextInTaskRunnerThread) { | 
 |   std::shared_ptr<fml::AutoResetWaitableEvent> latch = | 
 |       std::make_shared<fml::AutoResetWaitableEvent>(); | 
 |   fml::RefPtr<UnrefQueue<TestResourceContext>> unref_queue; | 
 |   fml::TaskQueueId dtor_task_queue_id(0); | 
 |   unref_task_runner()->PostTask([&]() { | 
 |     auto resource_context = | 
 |         sk_make_sp<TestResourceContext>(latch, &dtor_task_queue_id); | 
 |     unref_queue = fml::MakeRefCounted<UnrefQueue<TestResourceContext>>( | 
 |         unref_task_runner(), fml::TimeDelta::FromSeconds(0), | 
 |         std::move(resource_context)); | 
 |     latch->Signal(); | 
 |   }); | 
 |   latch->Wait(); | 
 |  | 
 |   // Delete the unref queue, it will schedule a task to unref the resource | 
 |   // context in the task runner's thread. | 
 |   unref_queue = nullptr; | 
 |   latch->Wait(); | 
 |   // Verify that the resource context was destroyed in the task runner's thread. | 
 |   ASSERT_EQ(dtor_task_queue_id, unref_task_runner()->GetTaskQueueId()); | 
 | } | 
 |  | 
 | }  // namespace testing | 
 | }  // namespace flutter |