| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cc/test/test_layer_tree_frame_sink.h" |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/functional/bind.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "cc/mojo_embedder/viz_layer_context.h" |
| #include "cc/test/test_client_shared_image_interface.h" |
| #include "cc/trees/layer_tree_frame_sink_client.h" |
| #include "cc/trees/single_thread_proxy.h" |
| #include "cc/trees/task_runner_provider.h" |
| #include "components/viz/common/features.h" |
| #include "components/viz/common/frame_sinks/begin_frame_args.h" |
| #include "components/viz/service/display/direct_renderer.h" |
| #include "components/viz/service/display/output_surface.h" |
| #include "components/viz/service/display/overlay_processor_stub.h" |
| #include "components/viz/service/display/skia_output_surface.h" |
| #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h" |
| #include "components/viz/test/test_gpu_service_holder.h" |
| #include "gpu/command_buffer/service/scheduler.h" |
| #include "gpu/command_buffer/service/shared_image/shared_image_manager.h" |
| #include "gpu/ipc/client/client_shared_image_interface.h" |
| #include "services/viz/public/mojom/compositing/compositor_frame_sink.mojom.h" |
| |
| namespace cc { |
| |
| class TestLayerTreeFrameSink::TestCompositorFrameSinkSupport |
| : public viz::CompositorFrameSinkSupport { |
| public: |
| TestCompositorFrameSinkSupport(viz::mojom::CompositorFrameSinkClient* client, |
| TestLayerTreeFrameSinkClient* test_client, |
| TaskRunnerProvider* task_runner_provider, |
| viz::FrameSinkManagerImpl* frame_sink_manager, |
| const viz::FrameSinkId& frame_sink_id, |
| bool is_root, |
| viz::Display* display) |
| : viz::CompositorFrameSinkSupport(client, |
| frame_sink_manager, |
| frame_sink_id, |
| is_root), |
| display_(display), |
| test_client_(test_client), |
| task_runner_provider_(task_runner_provider) {} |
| ~TestCompositorFrameSinkSupport() override = default; |
| |
| viz::SubmitResult MaybeSubmitCompositorFrame( |
| const viz::LocalSurfaceId& local_surface_id, |
| viz::CompositorFrame frame, |
| std::optional<viz::HitTestRegionList> hit_test_region_list, |
| uint64_t submit_time) override { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| test_client_->DisplayReceivedCompositorFrame(frame); |
| |
| CHECK(local_surface_id.is_valid()) << "Tests should ensure a valid LSIid"; |
| |
| if (last_submitted_display_size_ != frame.size_in_pixels() || |
| last_submitted_device_scale_factor_ != frame.device_scale_factor()) { |
| CHECK_NE(last_submitted_local_surface_id_, local_surface_id) |
| << "Tests should update LSIid when changing display size or scale"; |
| last_submitted_display_size_ = frame.size_in_pixels(); |
| last_submitted_device_scale_factor_ = frame.device_scale_factor(); |
| } |
| |
| last_submitted_local_surface_id_ = local_surface_id; |
| |
| // Ensure that the display's local surface ID and its size are initialized |
| // (note that these calls will be no-ops if already called for this surface |
| // ID/device scale factor/frame size on a previous invocation of |
| // SubmitCompositorFrame()). |
| display_->SetLocalSurfaceId(local_surface_id, frame.device_scale_factor()); |
| display_->Resize(frame.size_in_pixels()); |
| |
| auto result = viz::CompositorFrameSinkSupport::MaybeSubmitCompositorFrame( |
| local_surface_id, std::move(frame), hit_test_region_list, submit_time); |
| |
| if (!display_->has_scheduler()) { |
| // In synchronous mode, we manually issue DrawAndSwap. |
| viz::DrawAndSwapParams params; |
| params.expected_display_time = base::TimeTicks::Now(); |
| display_->DrawAndSwap(params); |
| } |
| return result; |
| } |
| |
| void SetParams(viz::mojom::CompositorFrameSinkParamsPtr params) { |
| if (params->wants_animate_only_begin_frames) { |
| SetWantsAnimateOnlyBeginFrames(); |
| } |
| if (params->auto_needs_begin_frame) { |
| SetAutoNeedsBeginFrame(); |
| } |
| if (params->no_compositor_frame_acks) { |
| SetNoCompositorFrameAcks(); |
| } |
| } |
| |
| private: |
| raw_ptr<viz::Display> display_; |
| raw_ptr<TestLayerTreeFrameSinkClient> test_client_ = nullptr; |
| raw_ptr<TaskRunnerProvider> task_runner_provider_; |
| gfx::Size last_submitted_display_size_; |
| float last_submitted_device_scale_factor_; |
| viz::LocalSurfaceId last_submitted_local_surface_id_; |
| }; |
| |
| class TestLayerTreeFrameSink::TestCompositorFrameSinkImpl |
| : public viz::mojom::CompositorFrameSink { |
| public: |
| explicit TestCompositorFrameSinkImpl( |
| viz::CompositorFrameSinkSupport* support, |
| mojo::PendingReceiver<viz::mojom::CompositorFrameSink> receiver) |
| : support_(support), receiver_(this, std::move(receiver)) {} |
| ~TestCompositorFrameSinkImpl() override = default; |
| |
| private: |
| // viz::mojom::CompositorFrameSink: |
| void SetParams(viz::mojom::CompositorFrameSinkParamsPtr params) override {} |
| void SetNeedsBeginFrame(bool needs_begin_frame) override {} |
| void SubmitCompositorFrame( |
| const viz::LocalSurfaceId& local_surface_id, |
| viz::CompositorFrame frame, |
| std::optional<viz::HitTestRegionList> hit_test_region_list, |
| uint64_t submit_time) override {} |
| void DidNotProduceFrame(const viz::BeginFrameAck& begin_frame_ack) override {} |
| void NotifyNewLocalSurfaceIdExpectedWhilePaused() override {} |
| void BindLayerContext(viz::mojom::PendingLayerContextPtr context, |
| viz::mojom::LayerContextSettingsPtr settings) override; |
| #if BUILDFLAG(IS_ANDROID) |
| void SetThreads(const std::vector<viz::Thread>& threads) override {} |
| #endif |
| |
| raw_ptr<viz::CompositorFrameSinkSupport> support_; |
| mojo::Receiver<viz::mojom::CompositorFrameSink> receiver_; |
| }; |
| |
| void TestLayerTreeFrameSink::TestCompositorFrameSinkImpl::BindLayerContext( |
| viz::mojom::PendingLayerContextPtr context, |
| viz::mojom::LayerContextSettingsPtr settings) { |
| support_->BindLayerContext(*context, std::move(settings)); |
| } |
| |
| static constexpr viz::FrameSinkId kLayerTreeFrameSinkId(1, 1); |
| |
| TestLayerTreeFrameSink::TestLayerTreeFrameSink( |
| scoped_refptr<viz::RasterContextProvider> compositor_context_provider, |
| scoped_refptr<viz::RasterContextProvider> worker_context_provider, |
| scoped_refptr<gpu::SharedImageInterface> shared_image_interface, |
| const viz::RendererSettings& renderer_settings, |
| const viz::DebugRendererSettings* const debug_settings, |
| TaskRunnerProvider* task_runner_provider, |
| bool synchronous_composite, |
| bool disable_display_vsync, |
| double refresh_rate, |
| viz::BeginFrameSource* begin_frame_source) |
| : LayerTreeFrameSink( |
| std::move(compositor_context_provider), |
| std::move(worker_context_provider), |
| task_runner_provider->HasImplThread() |
| ? task_runner_provider->ImplThreadTaskRunner() |
| : task_runner_provider->MainThreadTaskRunner(), |
| shared_image_interface |
| ? base::MakeRefCounted<TestClientSharedImageInterface>( |
| shared_image_interface) |
| : nullptr), |
| synchronous_composite_(synchronous_composite), |
| disable_display_vsync_(disable_display_vsync), |
| renderer_settings_(renderer_settings), |
| debug_settings_(debug_settings), |
| refresh_rate_(refresh_rate), |
| frame_sink_id_(kLayerTreeFrameSinkId), |
| client_provided_begin_frame_source_(begin_frame_source), |
| external_begin_frame_source_(this), |
| task_runner_provider_(task_runner_provider), |
| shared_image_interface_provider_( |
| shared_image_interface |
| ? std::make_unique<viz::TestSharedImageInterfaceProvider>( |
| shared_image_interface) |
| : std::make_unique<viz::TestSharedImageInterfaceProvider>()) { |
| } |
| |
| TestLayerTreeFrameSink::~TestLayerTreeFrameSink() = default; |
| |
| void TestLayerTreeFrameSink::SetDisplayColorSpace( |
| const gfx::ColorSpace& display_color_space) { |
| display_color_spaces_ = gfx::DisplayColorSpaces(display_color_space); |
| if (display_) |
| display_->SetDisplayColorSpaces(display_color_spaces_); |
| } |
| |
| viz::CompositorFrameSinkSupport* TestLayerTreeFrameSink::support() const { |
| return support_.get(); |
| } |
| |
| bool TestLayerTreeFrameSink::BindToClient(LayerTreeFrameSinkClient* client) { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| if (!LayerTreeFrameSink::BindToClient(client)) |
| return false; |
| |
| frame_sink_manager_ = std::make_unique<viz::FrameSinkManagerImpl>( |
| viz::FrameSinkManagerImpl::InitParams()); |
| frame_sink_manager_->SetSharedImageInterfaceProviderForTest( |
| shared_image_interface_provider_.get()); |
| |
| std::unique_ptr<viz::DisplayCompositorMemoryAndTaskController> |
| display_controller; |
| std::unique_ptr<viz::OutputSurface> display_output_surface; |
| const bool gpu_accelerated = context_provider(); |
| if (gpu_accelerated) { |
| display_controller = test_client_->CreateDisplayController(); |
| auto output_surface = |
| test_client_->CreateSkiaOutputSurface(display_controller.get()); |
| display_output_surface = std::move(output_surface); |
| } else { |
| display_output_surface = test_client_->CreateSoftwareOutputSurface(); |
| } |
| |
| std::unique_ptr<viz::DisplayScheduler> scheduler; |
| if (!synchronous_composite_) { |
| if (client_provided_begin_frame_source_) { |
| display_begin_frame_source_ = client_provided_begin_frame_source_; |
| } else if (disable_display_vsync_) { |
| begin_frame_source_ = std::make_unique<viz::BackToBackBeginFrameSource>( |
| std::make_unique<viz::DelayBasedTimeSource>( |
| compositor_task_runner_.get())); |
| display_begin_frame_source_ = begin_frame_source_.get(); |
| } else { |
| begin_frame_source_ = std::make_unique<viz::DelayBasedBeginFrameSource>( |
| std::make_unique<viz::DelayBasedTimeSource>( |
| compositor_task_runner_.get()), |
| viz::BeginFrameSource::kNotRestartableId); |
| begin_frame_source_->OnUpdateVSyncParameters( |
| base::TimeTicks::Now(), base::Milliseconds(1000.f / refresh_rate_)); |
| display_begin_frame_source_ = begin_frame_source_.get(); |
| } |
| scheduler = std::make_unique<viz::DisplayScheduler>( |
| display_begin_frame_source_, compositor_task_runner_.get(), |
| display_output_surface->capabilities().pending_swap_params); |
| } |
| |
| auto overlay_processor = std::make_unique<viz::OverlayProcessorStub>(); |
| // Normally display will need to take ownership of a |
| // gpu::GpuTaskschedulerhelper in order to keep it alive to share between the |
| // output surface and the overlay processor. In this case the overlay |
| // processor is only a stub, and viz::TestGpuServiceHolder will keep a |
| // gpu::GpuTaskSchedulerHelper alive for output surface to use, so there is no |
| // need to pass in an gpu::GpuTaskSchedulerHelper here. |
| |
| gpu::SharedImageManager* shared_image_manager = nullptr; |
| gpu::Scheduler* gpu_scheduler = nullptr; |
| |
| if (!LayerTreeFrameSink::context_provider()) { |
| viz::GpuServiceImpl* gpu_service = |
| viz::TestGpuServiceHolder::GetInstance()->gpu_service(); |
| shared_image_manager = gpu_service->shared_image_manager(); |
| gpu_scheduler = gpu_service->gpu_scheduler(); |
| } |
| |
| display_ = std::make_unique<viz::Display>( |
| shared_image_manager, gpu_scheduler, renderer_settings_, debug_settings_, |
| frame_sink_id_, std::move(display_controller), |
| std::move(display_output_surface), std::move(overlay_processor), |
| std::move(scheduler), compositor_task_runner_); |
| |
| constexpr bool is_root = true; |
| support_ = std::make_unique<TestCompositorFrameSinkSupport>( |
| this, test_client_, task_runner_provider_, frame_sink_manager_.get(), |
| frame_sink_id_, is_root, display_.get()); |
| auto params = viz::mojom::CompositorFrameSinkParams::New(); |
| params->wants_animate_only_begin_frames = true; |
| params->no_compositor_frame_acks = |
| base::FeatureList::IsEnabled(::features::kNoCompositorFrameAcks); |
| support_->SetParams(std::move(params)); |
| client_->SetBeginFrameSource(&external_begin_frame_source_); |
| if (display_begin_frame_source_) { |
| frame_sink_manager_->RegisterBeginFrameSource(display_begin_frame_source_, |
| frame_sink_id_); |
| } |
| display_->Initialize(this, frame_sink_manager_->surface_manager()); |
| display_->renderer_for_testing()->SetEnlargePassTextureAmountForTesting( |
| enlarge_pass_texture_amount_); |
| display_->SetDisplayColorSpaces(display_color_spaces_); |
| display_->SetVisible(true); |
| return true; |
| } |
| |
| void TestLayerTreeFrameSink::UnregisterBeginFrameSource() { |
| if (display_begin_frame_source_) { |
| frame_sink_manager_->UnregisterBeginFrameSource( |
| display_begin_frame_source_); |
| display_begin_frame_source_ = nullptr; |
| } |
| } |
| |
| void TestLayerTreeFrameSink::DetachFromClient() { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| |
| if (display_begin_frame_source_) { |
| frame_sink_manager_->UnregisterBeginFrameSource( |
| display_begin_frame_source_); |
| display_begin_frame_source_ = nullptr; |
| } |
| client_->SetBeginFrameSource(nullptr); |
| compositor_frame_sink_impl_.reset(); |
| compositor_frame_sink_remote_.reset(); |
| support_ = nullptr; |
| display_ = nullptr; |
| begin_frame_source_ = nullptr; |
| frame_sink_manager_ = nullptr; |
| test_client_ = nullptr; |
| LayerTreeFrameSink::DetachFromClient(); |
| } |
| |
| void TestLayerTreeFrameSink::SetLocalSurfaceId( |
| const viz::LocalSurfaceId& local_surface_id) { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| local_surface_id_ = local_surface_id; |
| test_client_->DisplayReceivedLocalSurfaceId(local_surface_id); |
| } |
| |
| std::unique_ptr<LayerContext> TestLayerTreeFrameSink::CreateLayerContext( |
| LayerTreeHostImpl& host_impl) { |
| compositor_frame_sink_impl_ = std::make_unique<TestCompositorFrameSinkImpl>( |
| support_.get(), |
| compositor_frame_sink_remote_.BindNewPipeAndPassReceiver()); |
| return std::make_unique<mojo_embedder::VizLayerContext>( |
| *compositor_frame_sink_remote_.get(), host_impl); |
| } |
| |
| void TestLayerTreeFrameSink::SubmitCompositorFrame(viz::CompositorFrame frame, |
| bool hit_test_data_changed) { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| DCHECK(frame.metadata.begin_frame_ack.has_damage); |
| DCHECK(frame.metadata.begin_frame_ack.frame_id.IsSequenceValid()); |
| |
| support_->SubmitCompositorFrame(local_surface_id_, std::move(frame), |
| std::nullopt, 0); |
| |
| if (!display_->has_scheduler()) { |
| // Post this to get a new stack frame so that we exit this function before |
| // calling the client to tell it that it is done. |
| compositor_task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&TestLayerTreeFrameSink::SendCompositorFrameAckToClient, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| } |
| |
| void TestLayerTreeFrameSink::DidNotProduceFrame(const viz::BeginFrameAck& ack, |
| FrameSkippedReason reason) { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| DCHECK(!ack.has_damage); |
| DCHECK(ack.frame_id.IsSequenceValid()); |
| // When this sink is detached, it'll destroy it's support_ and then |
| // TestInProcessContextProvider, which then destroys RasterInProcessContext, |
| // where a runloop can deliver a DidNotProduceFrame call to this sink class |
| // and trigger a nullptr crash without this check. |
| if (support_) { |
| support_->DidNotProduceFrame(ack); |
| } |
| } |
| |
| void TestLayerTreeFrameSink::DidReceiveCompositorFrameAck( |
| std::vector<viz::ReturnedResource> resources) { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| ReclaimResources(std::move(resources)); |
| // In synchronous mode, we manually send acks and this method should not be |
| // used. |
| if (!display_->has_scheduler()) |
| return; |
| client_->DidReceiveCompositorFrameAck(); |
| } |
| |
| void TestLayerTreeFrameSink::OnBeginFrame( |
| const viz::BeginFrameArgs& args, |
| const viz::FrameTimingDetailsMap& timing_details, |
| std::vector<viz::ReturnedResource> resources) { |
| if (!resources.empty()) { |
| ReclaimResources(std::move(resources)); |
| } |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| for (const auto& pair : timing_details) |
| client_->DidPresentCompositorFrame(pair.first, pair.second); |
| external_begin_frame_source_.OnBeginFrame(args); |
| } |
| |
| void TestLayerTreeFrameSink::ReclaimResources( |
| std::vector<viz::ReturnedResource> resources) { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| client_->ReclaimResources(std::move(resources)); |
| } |
| |
| void TestLayerTreeFrameSink::OnBeginFramePausedChanged(bool paused) { |
| external_begin_frame_source_.OnSetBeginFrameSourcePaused(paused); |
| } |
| |
| void TestLayerTreeFrameSink::DisplayOutputSurfaceLost() { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| client_->DidLoseLayerTreeFrameSink(); |
| } |
| |
| void TestLayerTreeFrameSink::DisplayWillDrawAndSwap( |
| bool will_draw_and_swap, |
| viz::AggregatedRenderPassList* render_passes) { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| test_client_->DisplayWillDrawAndSwap(will_draw_and_swap, render_passes); |
| } |
| |
| void TestLayerTreeFrameSink::DisplayDidDrawAndSwap() { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| test_client_->DisplayDidDrawAndSwap(); |
| } |
| |
| void TestLayerTreeFrameSink::DisplayDidReceiveCALayerParams( |
| gfx::CALayerParams ca_layer_params) {} |
| |
| void TestLayerTreeFrameSink::DisplayDidCompleteSwapWithSize( |
| const gfx::Size& pixel_Size) {} |
| |
| void TestLayerTreeFrameSink::DisplayAddChildWindowToBrowser( |
| gpu::SurfaceHandle child_window) {} |
| |
| void TestLayerTreeFrameSink::OnNeedsBeginFrames(bool needs_begin_frames) { |
| support_->SetNeedsBeginFrame(needs_begin_frames); |
| } |
| |
| void TestLayerTreeFrameSink::SendCompositorFrameAckToClient() { |
| DebugScopedSetImplThread impl(task_runner_provider_); |
| client_->DidReceiveCompositorFrameAck(); |
| } |
| |
| } // namespace cc |