| // Copyright 2019 The Chromium 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 "components/viz/demo/host/demo_host.h" |
| |
| #include <utility> |
| |
| #include "base/rand_util.h" |
| #include "components/viz/demo/client/demo_client.h" |
| #include "components/viz/host/renderer_settings_creation.h" |
| |
| namespace demo { |
| |
| DemoHost::DemoHost(gfx::AcceleratedWidget widget, |
| const gfx::Size& size, |
| viz::mojom::FrameSinkManagerClientRequest client_request, |
| viz::mojom::FrameSinkManagerPtr frame_sink_manager_ptr) |
| : widget_(widget), size_(size), thread_("DemoHost") { |
| CHECK(thread_.Start()); |
| thread_.task_runner()->PostTask( |
| FROM_HERE, base::BindOnce(&DemoHost::Initialize, base::Unretained(this), |
| std::move(client_request), |
| frame_sink_manager_ptr.PassInterface())); |
| } |
| |
| DemoHost::~DemoHost() = default; |
| |
| void DemoHost::Resize(const gfx::Size& size) { |
| thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(&DemoHost::ResizeOnThread, base::Unretained(this), size)); |
| } |
| |
| void DemoHost::ResizeOnThread(const gfx::Size& size) { |
| if (size_ == size) |
| return; |
| size_ = size; |
| display_private_->Resize(size); |
| |
| // Every size change for a client needs a new LocalSurfaceId. |
| allocator_.GenerateId(); |
| root_client_->Resize(size_, allocator_.GetCurrentLocalSurfaceIdAllocation()); |
| } |
| |
| void DemoHost::EmbedClients(DemoClient* embedder_client, |
| const gfx::Rect& child_bounds) { |
| // Generate a FrameSinkId for the client. Each client can have any number of |
| // frame-sinks, and these frame-sinks should share the same |client_id| |
| // component for the FrameSinkId. In this demo however, each client has a |
| // single FrameSink, so the client-id can just be randomly generated, and it |
| // doesn't make a difference. |
| uint64_t rand = base::RandUint64(); |
| viz::FrameSinkId frame_sink_id(rand >> 32, rand & 0xffffffff); |
| |
| // Register the frame sink and its hierarchy. |
| host_frame_sink_manager_.RegisterFrameSinkId( |
| frame_sink_id, this, viz::ReportFirstSurfaceActivation::kNo); |
| host_frame_sink_manager_.RegisterFrameSinkHierarchy( |
| embedder_client->frame_sink_id(), frame_sink_id); |
| |
| // Next, create a mojom::CompositorFrameSink for the client, so that the |
| // client is able to submit visual (and hit-test) content to the viz service. |
| // Note that in this demo app, the host is setting up the message-pipes, and |
| // then sending the end-points to the embedded-client and the viz-service. |
| // However, it is possible for the embedded-client to initiate the creation of |
| // the message-pipes, in which case, the client would need to send the |
| // service-end-points to the host (via a non-viz API), so that the host can in |
| // turn send them to the service. |
| viz::mojom::CompositorFrameSinkClientPtr client_ptr; |
| auto client_request = mojo::MakeRequest(&client_ptr); |
| |
| viz::mojom::CompositorFrameSinkPtr sink_ptr; |
| auto sink_request = mojo::MakeRequest(&sink_ptr); |
| |
| host_frame_sink_manager_.CreateCompositorFrameSink( |
| frame_sink_id, std::move(sink_request), std::move(client_ptr)); |
| |
| // At this point, the host is done setting everything up. Now it is up to the |
| // new client to take over the communication (i.e. the mojo message pipes) |
| // with the service for the frame-sink. The embedder (i.e. the parent client) |
| // also needs to know about the new client's FrameSinkId, so that it is able |
| // to embed it. Both the embedder and the embedded client also need to use |
| // the same LocalSurfaceId for the embedding. Typically, the embedder is the |
| // one that generates the LocalSurfaceId for the embedded client. However, it |
| // is possible for another source (e.g. the viz-host) to generate the |
| // LocalSurfaceId, and dispatch that separately to both the embedder and the |
| // embedded clients. There is no specific viz-API for communicating these |
| // FrameSinkId and LocalSurfaceId between these clients. In chrome, these |
| // happen through the content API (or through the window-service API in |
| // ChromeOS). |
| // In this demo app, the embedder-client is assigning the LocalSurfaceId |
| // (through DemoClient). |
| |
| auto lsid_allocation = embedder_client->Embed(frame_sink_id, child_bounds); |
| auto embedded_client = std::make_unique<DemoClient>( |
| frame_sink_id, lsid_allocation, child_bounds); |
| embedded_client->Initialize(std::move(client_request), |
| sink_ptr.PassInterface()); |
| if (embedder_client == root_client_.get()) { |
| // Embed another client after a second. This could embed the client |
| // immediately here too if desired. The delay is to demonstrate asynchronous |
| // usage of the API. |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&DemoHost::EmbedClients, base::Unretained(this), |
| embedded_client.get(), gfx::Rect(125, 125, 150, 150)), |
| base::TimeDelta::FromSeconds(1)); |
| } |
| embedded_clients_.push_back(std::move(embedded_client)); |
| } |
| |
| void DemoHost::Initialize(viz::mojom::FrameSinkManagerClientRequest request, |
| viz::mojom::FrameSinkManagerPtrInfo ptr_info) { |
| host_frame_sink_manager_.BindAndSetManager( |
| std::move(request), nullptr, |
| viz::mojom::FrameSinkManagerPtr(std::move(ptr_info))); |
| |
| display_client_ = std::make_unique<viz::HostDisplayClient>(widget_); |
| |
| auto root_params = viz::mojom::RootCompositorFrameSinkParams::New(); |
| |
| // Create interfaces for a root CompositorFrameSink. |
| viz::mojom::CompositorFrameSinkAssociatedPtrInfo sink_info; |
| root_params->compositor_frame_sink = mojo::MakeRequest(&sink_info); |
| auto client_request = |
| mojo::MakeRequest(&root_params->compositor_frame_sink_client); |
| root_params->display_private = mojo::MakeRequest(&display_private_); |
| root_params->display_client = |
| display_client_->GetBoundPtr(nullptr).PassInterface(); |
| |
| constexpr viz::FrameSinkId root_frame_sink_id(0xdead, 0xbeef); |
| root_params->frame_sink_id = root_frame_sink_id; |
| root_params->widget = widget_; |
| root_params->gpu_compositing = false; |
| root_params->renderer_settings = viz::CreateRendererSettings(); |
| |
| host_frame_sink_manager_.RegisterFrameSinkId( |
| root_params->frame_sink_id, this, viz::ReportFirstSurfaceActivation::kNo); |
| host_frame_sink_manager_.CreateRootCompositorFrameSink( |
| std::move(root_params)); |
| |
| display_private_->Resize(size_); |
| display_private_->SetDisplayVisible(true); |
| |
| // Initialize as a client now, since the host has to submit compositor frames |
| // like any other clients. |
| // The 'root' is not embedded by anything else. However, it still needs to |
| // have a valid LocalSurfaceId, which changes when root changes size (or |
| // device-scale-factor etc.). |
| allocator_.GenerateId(); |
| root_client_ = std::make_unique<DemoClient>( |
| root_frame_sink_id, allocator_.GetCurrentLocalSurfaceIdAllocation(), |
| gfx::Rect(size_)); |
| root_client_->Initialize(std::move(client_request), std::move(sink_info)); |
| |
| // Embed a new client into the root after the first second. |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&DemoHost::EmbedClients, base::Unretained(this), |
| root_client_.get(), gfx::Rect(50, 50, 300, 300)), |
| base::TimeDelta::FromSeconds(1)); |
| } |
| |
| void DemoHost::OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) {} |
| |
| void DemoHost::OnFrameTokenChanged(uint32_t frame_token) {} |
| |
| } // namespace demo |