|
7 | 7 | #include "flutter/shell/common/rasterizer.h"
|
8 | 8 |
|
9 | 9 | #include <memory>
|
| 10 | +#include <optional> |
10 | 11 |
|
11 | 12 | #include "flutter/flow/frame_timings.h"
|
12 | 13 | #include "flutter/fml/synchronization/count_down_latch.h"
|
@@ -777,7 +778,7 @@ TEST(RasterizerTest,
|
777 | 778 | framebuffer_info.supports_readback = true;
|
778 | 779 | return std::make_unique<SurfaceFrame>(
|
779 | 780 | /*surface=*/nullptr, framebuffer_info,
|
780 |
| - /*submit_callback=*/[](const SurfaceFrame&, SkCanvas*) { |
| 781 | + /*submit_callback=*/[](const SurfaceFrame& frame, SkCanvas*) { |
781 | 782 | return true;
|
782 | 783 | });
|
783 | 784 | }));
|
@@ -830,4 +831,152 @@ TEST(RasterizerTest,
|
830 | 831 | latch.Wait();
|
831 | 832 | }
|
832 | 833 |
|
| 834 | +TEST(RasterizerTest, presentationTimeSetWhenVsyncTargetInFuture) { |
| 835 | + std::string test_name = |
| 836 | + ::testing::UnitTest::GetInstance()->current_test_info()->name(); |
| 837 | + ThreadHost thread_host("io.flutter.test." + test_name + ".", |
| 838 | + ThreadHost::Type::Platform | ThreadHost::Type::RASTER | |
| 839 | + ThreadHost::Type::IO | ThreadHost::Type::UI); |
| 840 | + TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), |
| 841 | + thread_host.raster_thread->GetTaskRunner(), |
| 842 | + thread_host.ui_thread->GetTaskRunner(), |
| 843 | + thread_host.io_thread->GetTaskRunner()); |
| 844 | + MockDelegate delegate; |
| 845 | + ON_CALL(delegate, GetTaskRunners()).WillByDefault(ReturnRef(task_runners)); |
| 846 | + |
| 847 | + fml::AutoResetWaitableEvent latch; |
| 848 | + std::unique_ptr<Rasterizer> rasterizer; |
| 849 | + thread_host.raster_thread->GetTaskRunner()->PostTask([&] { |
| 850 | + rasterizer = std::make_unique<Rasterizer>(delegate); |
| 851 | + latch.Signal(); |
| 852 | + }); |
| 853 | + latch.Wait(); |
| 854 | + |
| 855 | + const auto millis_16 = fml::TimeDelta::FromMilliseconds(16); |
| 856 | + const auto first_timestamp = fml::TimePoint::Now() + millis_16; |
| 857 | + auto second_timestamp = first_timestamp + millis_16; |
| 858 | + std::vector<fml::TimePoint> timestamps = {first_timestamp, second_timestamp}; |
| 859 | + |
| 860 | + int frames_submitted = 0; |
| 861 | + fml::CountDownLatch submit_latch(2); |
| 862 | + auto surface = std::make_unique<MockSurface>(); |
| 863 | + ON_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); |
| 864 | + ON_CALL(*surface, AcquireFrame(SkISize())) |
| 865 | + .WillByDefault(::testing::Invoke([&] { |
| 866 | + SurfaceFrame::FramebufferInfo framebuffer_info; |
| 867 | + framebuffer_info.supports_readback = true; |
| 868 | + return std::make_unique<SurfaceFrame>( |
| 869 | + /*surface=*/nullptr, framebuffer_info, |
| 870 | + /*submit_callback=*/[&](const SurfaceFrame& frame, SkCanvas*) { |
| 871 | + const auto pres_time = *frame.submit_info().presentation_time; |
| 872 | + const auto diff = pres_time - first_timestamp; |
| 873 | + int num_frames_submitted = frames_submitted++; |
| 874 | + EXPECT_EQ(diff.ToMilliseconds(), |
| 875 | + num_frames_submitted * millis_16.ToMilliseconds()); |
| 876 | + submit_latch.CountDown(); |
| 877 | + return true; |
| 878 | + }); |
| 879 | + })); |
| 880 | + |
| 881 | + ON_CALL(*surface, MakeRenderContextCurrent()) |
| 882 | + .WillByDefault(::testing::Invoke( |
| 883 | + [] { return std::make_unique<GLContextDefaultResult>(true); })); |
| 884 | + |
| 885 | + thread_host.raster_thread->GetTaskRunner()->PostTask([&] { |
| 886 | + rasterizer->Setup(std::move(surface)); |
| 887 | + auto pipeline = std::make_shared<LayerTreePipeline>(/*depth=*/10); |
| 888 | + for (int i = 0; i < 2; i++) { |
| 889 | + auto layer_tree = |
| 890 | + std::make_unique<LayerTree>(/*frame_size=*/SkISize(), |
| 891 | + /*device_pixel_ratio=*/2.0f); |
| 892 | + auto layer_tree_item = std::make_unique<LayerTreeItem>( |
| 893 | + std::move(layer_tree), CreateFinishedBuildRecorder(timestamps[i])); |
| 894 | + PipelineProduceResult result = |
| 895 | + pipeline->Produce().Complete(std::move(layer_tree_item)); |
| 896 | + EXPECT_TRUE(result.success); |
| 897 | + EXPECT_EQ(result.is_first_item, i == 0); |
| 898 | + } |
| 899 | + auto no_discard = [](LayerTree&) { return false; }; |
| 900 | + // Although we only call 'Rasterizer::Draw' once, it will be called twice |
| 901 | + // finally because there are two items in the pipeline. |
| 902 | + rasterizer->Draw(pipeline, no_discard); |
| 903 | + }); |
| 904 | + |
| 905 | + submit_latch.Wait(); |
| 906 | + thread_host.raster_thread->GetTaskRunner()->PostTask([&] { |
| 907 | + rasterizer.reset(); |
| 908 | + latch.Signal(); |
| 909 | + }); |
| 910 | + latch.Wait(); |
| 911 | +} |
| 912 | + |
| 913 | +TEST(RasterizerTest, presentationTimeNotSetWhenVsyncTargetInPast) { |
| 914 | + std::string test_name = |
| 915 | + ::testing::UnitTest::GetInstance()->current_test_info()->name(); |
| 916 | + ThreadHost thread_host("io.flutter.test." + test_name + ".", |
| 917 | + ThreadHost::Type::Platform | ThreadHost::Type::RASTER | |
| 918 | + ThreadHost::Type::IO | ThreadHost::Type::UI); |
| 919 | + TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(), |
| 920 | + thread_host.raster_thread->GetTaskRunner(), |
| 921 | + thread_host.ui_thread->GetTaskRunner(), |
| 922 | + thread_host.io_thread->GetTaskRunner()); |
| 923 | + MockDelegate delegate; |
| 924 | + ON_CALL(delegate, GetTaskRunners()).WillByDefault(ReturnRef(task_runners)); |
| 925 | + |
| 926 | + fml::AutoResetWaitableEvent latch; |
| 927 | + std::unique_ptr<Rasterizer> rasterizer; |
| 928 | + thread_host.raster_thread->GetTaskRunner()->PostTask([&] { |
| 929 | + rasterizer = std::make_unique<Rasterizer>(delegate); |
| 930 | + latch.Signal(); |
| 931 | + }); |
| 932 | + latch.Wait(); |
| 933 | + |
| 934 | + const auto millis_16 = fml::TimeDelta::FromMilliseconds(16); |
| 935 | + const auto first_timestamp = fml::TimePoint::Now() - millis_16; |
| 936 | + |
| 937 | + fml::CountDownLatch submit_latch(1); |
| 938 | + auto surface = std::make_unique<MockSurface>(); |
| 939 | + ON_CALL(*surface, AllowsDrawingWhenGpuDisabled()).WillByDefault(Return(true)); |
| 940 | + ON_CALL(*surface, AcquireFrame(SkISize())) |
| 941 | + .WillByDefault(::testing::Invoke([&] { |
| 942 | + SurfaceFrame::FramebufferInfo framebuffer_info; |
| 943 | + framebuffer_info.supports_readback = true; |
| 944 | + return std::make_unique<SurfaceFrame>( |
| 945 | + /*surface=*/nullptr, framebuffer_info, |
| 946 | + /*submit_callback=*/[&](const SurfaceFrame& frame, SkCanvas*) { |
| 947 | + const std::optional<fml::TimePoint> pres_time = |
| 948 | + frame.submit_info().presentation_time; |
| 949 | + EXPECT_EQ(pres_time, std::nullopt); |
| 950 | + submit_latch.CountDown(); |
| 951 | + return true; |
| 952 | + }); |
| 953 | + })); |
| 954 | + |
| 955 | + ON_CALL(*surface, MakeRenderContextCurrent()) |
| 956 | + .WillByDefault(::testing::Invoke( |
| 957 | + [] { return std::make_unique<GLContextDefaultResult>(true); })); |
| 958 | + |
| 959 | + thread_host.raster_thread->GetTaskRunner()->PostTask([&] { |
| 960 | + rasterizer->Setup(std::move(surface)); |
| 961 | + auto pipeline = std::make_shared<LayerTreePipeline>(/*depth=*/10); |
| 962 | + auto layer_tree = std::make_unique<LayerTree>(/*frame_size=*/SkISize(), |
| 963 | + /*device_pixel_ratio=*/2.0f); |
| 964 | + auto layer_tree_item = std::make_unique<LayerTreeItem>( |
| 965 | + std::move(layer_tree), CreateFinishedBuildRecorder(first_timestamp)); |
| 966 | + PipelineProduceResult result = |
| 967 | + pipeline->Produce().Complete(std::move(layer_tree_item)); |
| 968 | + EXPECT_TRUE(result.success); |
| 969 | + EXPECT_EQ(result.is_first_item, true); |
| 970 | + auto no_discard = [](LayerTree&) { return false; }; |
| 971 | + rasterizer->Draw(pipeline, no_discard); |
| 972 | + }); |
| 973 | + |
| 974 | + submit_latch.Wait(); |
| 975 | + thread_host.raster_thread->GetTaskRunner()->PostTask([&] { |
| 976 | + rasterizer.reset(); |
| 977 | + latch.Signal(); |
| 978 | + }); |
| 979 | + latch.Wait(); |
| 980 | +} |
| 981 | + |
833 | 982 | } // namespace flutter
|
0 commit comments