blob: 9bb5bbd5e2b2099022d500f7660ae263d9ffc980 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// 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/command_line.h"
#include "base/rand_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "components/viz/demo/client/demo_client.h"
#include "components/viz/demo/common/switches.h"
#include "components/viz/host/renderer_settings_creation.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
namespace demo {
DemoHost::DemoHost(
gfx::AcceleratedWidget widget,
const gfx::Size& size,
mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient> client_receiver,
mojo::PendingRemote<viz::mojom::FrameSinkManager> frame_sink_manager_remote)
: 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_receiver),
std::move(frame_sink_manager_remote)));
}
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_.GetCurrentLocalSurfaceId());
}
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.
mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient> client_remote;
auto client_receiver = client_remote.InitWithNewPipeAndPassReceiver();
mojo::PendingRemote<viz::mojom::CompositorFrameSink> sink_remote;
auto sink_receiver = sink_remote.InitWithNewPipeAndPassReceiver();
host_frame_sink_manager_.CreateCompositorFrameSink(
frame_sink_id, std::move(sink_receiver), std::move(client_remote));
// 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_receiver),
std::move(sink_remote));
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::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DemoHost::EmbedClients, base::Unretained(this),
embedded_client.get(), gfx::Rect(125, 125, 150, 150)),
base::Seconds(1));
}
embedded_clients_.push_back(std::move(embedded_client));
}
void DemoHost::Initialize(
mojo::PendingReceiver<viz::mojom::FrameSinkManagerClient> receiver,
mojo::PendingRemote<viz::mojom::FrameSinkManager> remote) {
host_frame_sink_manager_.BindAndSetManager(std::move(receiver), nullptr,
std::move(remote));
display_client_ = std::make_unique<viz::HostDisplayClient>(widget_);
auto root_params = viz::mojom::RootCompositorFrameSinkParams::New();
// Create interfaces for a root CompositorFrameSink.
mojo::PendingAssociatedRemote<viz::mojom::CompositorFrameSink> sink_remote;
root_params->compositor_frame_sink =
sink_remote.InitWithNewEndpointAndPassReceiver();
auto client_receiver = root_params->compositor_frame_sink_client
.InitWithNewPipeAndPassReceiver();
root_params->display_private =
display_private_.BindNewEndpointAndPassReceiver();
root_params->display_client = display_client_->GetBoundRemote(nullptr);
constexpr viz::FrameSinkId root_frame_sink_id(0xdead, 0xbeef);
root_params->frame_sink_id = root_frame_sink_id;
root_params->widget = widget_;
base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
root_params->gpu_compositing = cmd_line->HasSwitch(switches::kVizDemoUseGPU);
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_.GetCurrentLocalSurfaceId(),
gfx::Rect(size_));
root_client_->Initialize(std::move(client_receiver), std::move(sink_remote));
// Embed a new client into the root after the first second.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&DemoHost::EmbedClients, base::Unretained(this),
root_client_.get(), gfx::Rect(50, 50, 300, 300)),
base::Seconds(1));
}
void DemoHost::OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) {}
void DemoHost::OnFrameTokenChanged(uint32_t frame_token,
base::TimeTicks activation_time) {}
} // namespace demo