| // 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 <utility> |
| |
| #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" |
| #include "third_party/skia/include/gpu/GrTypes.h" |
| |
| namespace flutter { |
| namespace testing { |
| |
| class TestSkObject : public SkRefCnt { |
| public: |
| TestSkObject(std::shared_ptr<fml::AutoResetWaitableEvent> latch, |
| fml::TaskQueueId* dtor_task_queue_id) |
| : latch_(std::move(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(std::move(latch), dtor_task_queue_id) {} |
| ~TestResourceContext() = default; |
| void performDeferredCleanup(std::chrono::milliseconds msNotUsed) {} |
| void deleteBackendTexture(const GrBackendTexture& texture) {} |
| void flushAndSubmit(GrSyncCpu sync) {} |
| }; |
| |
| 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> queues_created; |
| unref_task_runner_->PostTask([this, &queues_created]() { |
| 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)); |
| queues_created.set_value(true); |
| }); |
| queues_created.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 |