blob: e05871da1513ad691a343cdc2027164d9fcf3d55 [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/client/demo_client.h"
#include <array>
#include <memory>
#include <utility>
#include <vector>
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/quads/surface_draw_quad.h"
namespace demo {
DemoClient::DemoClient(const viz::FrameSinkId& frame_sink_id,
const viz::LocalSurfaceId& local_surface_id,
const gfx::Rect& bounds)
: thread_(frame_sink_id.ToString()),
frame_sink_id_(frame_sink_id),
local_surface_id_(local_surface_id),
bounds_(bounds) {
CHECK(thread_.Start());
}
DemoClient::~DemoClient() = default;
void DemoClient::Initialize(
mojo::PendingReceiver<viz::mojom::CompositorFrameSinkClient> receiver,
mojo::PendingAssociatedRemote<viz::mojom::CompositorFrameSink>
sink_remote) {
thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&DemoClient::InitializeOnThread,
base::Unretained(this), std::move(receiver),
std::move(sink_remote), mojo::NullRemote()));
}
void DemoClient::Initialize(
mojo::PendingReceiver<viz::mojom::CompositorFrameSinkClient> receiver,
mojo::PendingRemote<viz::mojom::CompositorFrameSink> sink_remote) {
thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&DemoClient::InitializeOnThread, base::Unretained(this),
std::move(receiver), mojo::NullAssociatedRemote(),
std::move(sink_remote)));
}
viz::LocalSurfaceId DemoClient::Embed(const viz::FrameSinkId& frame_sink_id,
const gfx::Rect& bounds) {
// |embeds_| is used on the client-thread in CreateFrame(). So this needs to
// be mutated under a lock.
base::AutoLock lock(lock_);
allocator_.GenerateId();
embeds_[frame_sink_id] = {allocator_.GetCurrentLocalSurfaceId(), bounds};
return embeds_[frame_sink_id].lsid;
}
void DemoClient::Resize(const gfx::Size& size,
const viz::LocalSurfaceId& local_surface_id) {
// |bounds_| and |local_surface_id_| are used on the client-thread in
// CreateFrame(). So these need to be mutated under a lock.
base::AutoLock lock(lock_);
bounds_.set_size(size);
local_surface_id_ = local_surface_id;
}
viz::CompositorFrame DemoClient::CreateFrame(const viz::BeginFrameArgs& args) {
constexpr auto colors = std::to_array<SkColor4f>({
SkColors::kRed,
SkColors::kGreen,
SkColors::kYellow,
});
viz::CompositorFrame frame;
frame.metadata.begin_frame_ack = viz::BeginFrameAck(args, true);
frame.metadata.device_scale_factor = 1.f;
frame.metadata.frame_token = ++next_frame_token_;
const viz::CompositorRenderPassId kRenderPassId{1};
const gfx::Rect& output_rect = bounds_;
const gfx::Rect& damage_rect = output_rect;
auto render_pass = viz::CompositorRenderPass::Create();
render_pass->SetNew(kRenderPassId, output_rect, damage_rect,
gfx::Transform());
// The content of the client is one big solid-color rectangle, which includes
// the other clients above it (in z-order). The embedded clients are first
// added to the CompositorFrame using their SurfaceId (i.e. the FrameSinkId
// and LocalSurfaceId), and then the big rectangle is added afterwards.
for (auto& iter : embeds_) {
const gfx::Rect& child_bounds = iter.second.bounds;
const gfx::Vector2dF center(child_bounds.width() / 2,
child_bounds.height() / 2);
// Apply a rotation so there's visual-update every frame in the demo.
gfx::Transform transform;
transform.Translate(center + child_bounds.OffsetFromOrigin());
transform.Rotate(iter.second.degrees);
iter.second.degrees += 0.3;
transform.Translate(-center);
viz::SharedQuadState* quad_state =
render_pass->CreateAndAppendSharedQuadState();
quad_state->SetAll(transform,
/*layer_rect=*/child_bounds,
/*visible_layer_rect=*/child_bounds,
/*filter_info=*/gfx::MaskFilterInfo(),
/*clip=*/std::nullopt,
/*contents_opaque=*/false, /*opacity_f=*/1.f,
/*blend=*/SkBlendMode::kSrcOver,
/*sorting_context=*/0,
/*layer_id=*/0u, /*fast_rounded_corner=*/false);
viz::SurfaceDrawQuad* embed =
render_pass->CreateAndAppendDrawQuad<viz::SurfaceDrawQuad>();
viz::SurfaceId surface_id(iter.first, iter.second.lsid);
// |rect| and |visible_rect| needs to be in the quad's coord-space, so to
// draw the whole quad, it needs to use origin (0, 0).
embed->SetNew(quad_state,
/*rect=*/gfx::Rect(child_bounds.size()),
/*visible_rect=*/gfx::Rect(child_bounds.size()),
viz::SurfaceRange(surface_id), SkColors::kGray,
/*stretch_content_to_fill_bounds=*/false);
}
// Add a solid-color draw-quad for the big rectangle covering the entire
// content-area of the client.
viz::SharedQuadState* quad_state =
render_pass->CreateAndAppendSharedQuadState();
quad_state->SetAll(gfx::Transform(),
/*quad_layer_rect=*/output_rect,
/*visible_layer_rect=*/output_rect,
/*mask_filter_info=*/gfx::MaskFilterInfo(),
/*clip_rect=*/std::nullopt, /*are_contents_opaque=*/false,
/*opacity=*/1.f,
/*blend_mode=*/SkBlendMode::kSrcOver,
/*sorting_context=*/0,
/*layer_id=*/0u,
/*fast_rounded_corner=*/false);
viz::SolidColorDrawQuad* color_quad =
render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
color_quad->SetNew(quad_state, output_rect, output_rect,
colors[(++frame_count_ / 60) % std::size(colors)], false);
frame.render_pass_list.push_back(std::move(render_pass));
return frame;
}
viz::mojom::CompositorFrameSink* DemoClient::GetPtr() {
if (associated_sink_.is_bound())
return associated_sink_.get();
return sink_.get();
}
void DemoClient::InitializeOnThread(
mojo::PendingReceiver<viz::mojom::CompositorFrameSinkClient> receiver,
mojo::PendingAssociatedRemote<viz::mojom::CompositorFrameSink>
associated_sink_remote,
mojo::PendingRemote<viz::mojom::CompositorFrameSink> sink_remote) {
receiver_.Bind(std::move(receiver));
if (associated_sink_remote)
associated_sink_.Bind(std::move(associated_sink_remote));
else
sink_.Bind(std::move(sink_remote));
// Request for begin-frames.
GetPtr()->SetNeedsBeginFrame(true);
}
void DemoClient::DidReceiveCompositorFrameAck(
std::vector<viz::ReturnedResource> resources) {
// See documentation in mojom for how this can be used.
}
void DemoClient::OnBeginFrame(const viz::BeginFrameArgs& args,
const viz::FrameTimingDetailsMap& timing_details,
std::vector<viz::ReturnedResource> resources) {
// Generate a new compositor-frame for each begin-frame. This demo client
// generates and submits the compositor-frame immediately. But it is possible
// for the client to delay sending the compositor-frame. |args| includes the
// deadline for the client before it needs to submit the compositor-frame.
base::AutoLock lock(lock_);
GetPtr()->SubmitCompositorFrame(local_surface_id_, CreateFrame(args),
/*hit_test_region_list=*/std::nullopt,
/*submit_time=*/0);
}
void DemoClient::OnBeginFramePausedChanged(bool paused) {}
void DemoClient::ReclaimResources(
std::vector<viz::ReturnedResource> resources) {}
} // namespace demo