blob: 74a2c2ed4e06a4532d5db554c8cae2ab82a9cddc [file] [log] [blame]
// Copyright 2016 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 "services/ui/surfaces/display_compositor.h"
#include <inttypes.h>
#include <string>
#include <utility>
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/strings/stringprintf.h"
#include "cc/ipc/display_compositor.mojom.h"
#include "cc/surfaces/surface_id.h"
#include "cc/surfaces/surface_observer.h"
#include "cc/surfaces/surface_reference.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/ui/common/task_runner_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/size.h"
namespace ui {
namespace test {
namespace {
std::string SurfaceIdString(const cc::SurfaceId& surface_id) {
return base::StringPrintf("%u:%u:%u", surface_id.frame_sink_id().client_id(),
surface_id.frame_sink_id().sink_id(),
surface_id.local_frame_id().local_id());
}
cc::SurfaceId MakeSurfaceId(uint32_t client_id,
uint32_t sink_id,
uint32_t local_id) {
return cc::SurfaceId(
cc::FrameSinkId(client_id, sink_id),
cc::LocalFrameId(local_id, base::UnguessableToken::Deserialize(0, 1u)));
}
// Test mojom::DisplayCompositorClient that records OnSurfaceCreated() events.
class TestDisplayCompositorClient : public cc::mojom::DisplayCompositorClient {
public:
TestDisplayCompositorClient() : binding_(this) {}
~TestDisplayCompositorClient() override {}
cc::mojom::DisplayCompositorClientPtr GetPtr() {
return binding_.CreateInterfacePtrAndBind();
}
// Returns events that have occurred and clear.
std::string events() {
std::string value = std::move(events_);
events_.clear();
return value;
}
private:
void AddEvent(const std::string& text) {
if (!events_.empty())
events_ += ";";
events_ += text;
}
// cc::mojom::DisplayCompositorClient:
void OnDisplayCompositorCreated(
const cc::SurfaceId& root_surface_id) override {
got_root_surface_id_ = true;
}
void OnSurfaceCreated(const cc::SurfaceId& surface_id,
const gfx::Size& frame_size,
float device_scale_factor) override {
EXPECT_TRUE(got_root_surface_id_);
AddEvent(base::StringPrintf("OnSurfaceCreated(%s)",
SurfaceIdString(surface_id).c_str()));
}
mojo::Binding<cc::mojom::DisplayCompositorClient> binding_;
std::string events_;
bool got_root_surface_id_ = false;
DISALLOW_COPY_AND_ASSIGN(TestDisplayCompositorClient);
};
// Test SurfaceReferenceManager that records AddSurfaceReference() and
// RemoveSurfaceReference() events.
class TestSurfaceReferenceManager : public cc::SurfaceReferenceManager {
public:
~TestSurfaceReferenceManager() override {}
const cc::SurfaceId& GetRootSurfaceId() const override { return root_id_; }
void AddSurfaceReference(const cc::SurfaceId& parent_id,
const cc::SurfaceId& child_id) override {
AddEvent(base::StringPrintf("Add(%s-%s)",
SurfaceIdString(parent_id).c_str(),
SurfaceIdString(child_id).c_str()));
}
void RemoveSurfaceReference(const cc::SurfaceId& parent_id,
const cc::SurfaceId& child_id) override {
AddEvent(base::StringPrintf("Remove(%s-%s)",
SurfaceIdString(parent_id).c_str(),
SurfaceIdString(child_id).c_str()));
}
size_t GetSurfaceReferenceCount(
const cc::SurfaceId& surface_id) const override {
NOTREACHED();
return 0;
}
size_t GetReferencedSurfaceCount(
const cc::SurfaceId& surface_id) const override {
NOTREACHED();
return 0;
}
// Returns events that have occurred and clear.
std::string events() {
std::string value = std::move(events_);
events_.clear();
return value;
}
private:
void AddEvent(const std::string& text) {
if (!events_.empty())
events_ += ";";
events_ += text;
}
const cc::SurfaceId root_id_ = MakeSurfaceId(0, 0, 0);
std::string events_;
};
} // namespace
class DisplayCompositorTest : public TaskRunnerTestBase {
public:
DisplayCompositorTest() {}
~DisplayCompositorTest() override {}
cc::SurfaceObserver* surface_observer() { return display_compositor_.get(); }
const cc::SurfaceId& GetRootSurfaceId() const {
return reference_manager_.GetRootSurfaceId();
}
void AddSurfaceReference(const cc::SurfaceId& parent_id,
const cc::SurfaceId& child_id) {
display_compositor_->AddSurfaceReferences(std::vector<cc::SurfaceReference>{
cc::SurfaceReference(parent_id, child_id)});
}
// Returns the total number of temporary references held by DisplayCompositor.
size_t CountTempReferences() {
size_t size = 0;
for (auto& map_entry : display_compositor_->temp_references_) {
size += map_entry.second.size();
}
return size;
}
// TaskRunnerTestBase:
void SetUp() override {
TaskRunnerTestBase::SetUp();
display_compositor_ = base::MakeUnique<DisplayCompositor>(
nullptr, nullptr, nullptr, nullptr, client_.GetPtr());
display_compositor_->reference_manager_ = &reference_manager_;
}
void TearDown() override {
// Clear any events before the next test.
client_.events();
reference_manager_.events();
}
protected:
TestDisplayCompositorClient client_;
TestSurfaceReferenceManager reference_manager_;
std::unique_ptr<DisplayCompositor> display_compositor_;
private:
DISALLOW_COPY_AND_ASSIGN(DisplayCompositorTest);
};
TEST_F(DisplayCompositorTest, AddSurfaceThenReference) {
const cc::SurfaceId parent_id = MakeSurfaceId(1, 1, 1);
const cc::SurfaceId surface_id = MakeSurfaceId(2, 1, 1);
surface_observer()->OnSurfaceCreated(surface_id, gfx::Size(1, 1), 1.0f);
RunUntilIdle();
// Client should get OnSurfaceCreated call and temporary reference added.
EXPECT_EQ("OnSurfaceCreated(2:1:1)", client_.events());
EXPECT_EQ("Add(0:0:0-2:1:1)", reference_manager_.events());
EXPECT_EQ(1u, CountTempReferences());
AddSurfaceReference(parent_id, surface_id);
RunUntilIdle();
// Real reference is added then temporary reference removed.
EXPECT_EQ("Add(1:1:1-2:1:1);Remove(0:0:0-2:1:1)",
reference_manager_.events());
EXPECT_EQ(0u, CountTempReferences());
}
TEST_F(DisplayCompositorTest, AddSurfaceThenRootReference) {
const cc::SurfaceId surface_id = MakeSurfaceId(1, 1, 1);
surface_observer()->OnSurfaceCreated(surface_id, gfx::Size(1, 1), 1.0f);
RunUntilIdle();
// Temporary reference should be added.
EXPECT_EQ("Add(0:0:0-1:1:1)", reference_manager_.events());
EXPECT_EQ(1u, CountTempReferences());
AddSurfaceReference(GetRootSurfaceId(), surface_id);
RunUntilIdle();
// Adding real reference doesn't need to change anything in
// SurfaceReferenceManager does remove the temporary reference marker.
EXPECT_EQ("", reference_manager_.events());
EXPECT_EQ(0u, CountTempReferences());
}
TEST_F(DisplayCompositorTest, AddTwoSurfacesThenOneReference) {
const cc::SurfaceId parent_id = MakeSurfaceId(1, 1, 1);
const cc::SurfaceId surface_id1 = MakeSurfaceId(2, 1, 1);
const cc::SurfaceId surface_id2 = MakeSurfaceId(3, 1, 1);
// Add two surfaces with different FrameSinkIds.
surface_observer()->OnSurfaceCreated(surface_id1, gfx::Size(1, 1), 1.0f);
surface_observer()->OnSurfaceCreated(surface_id2, gfx::Size(1, 1), 1.0f);
RunUntilIdle();
// Temporary reference should be added for both surfaces.
EXPECT_EQ("Add(0:0:0-2:1:1);Add(0:0:0-3:1:1)", reference_manager_.events());
EXPECT_EQ(2u, CountTempReferences());
AddSurfaceReference(parent_id, surface_id1);
RunUntilIdle();
// Real reference is added then temporary reference removed for 2:1:1. There
// should still be a temporary reference left to 3:1:1
EXPECT_EQ("Add(1:1:1-2:1:1);Remove(0:0:0-2:1:1)",
reference_manager_.events());
EXPECT_EQ(1u, CountTempReferences());
}
TEST_F(DisplayCompositorTest, AddSurfacesSkipReference) {
const cc::SurfaceId parent_id = MakeSurfaceId(1, 1, 1);
const cc::SurfaceId surface_id1 = MakeSurfaceId(2, 1, 1);
const cc::SurfaceId surface_id2 = MakeSurfaceId(2, 1, 2);
// Add two surfaces that have the same FrameSinkId. This would happen when a
// client submits two CFs before parent submits a new CF.
surface_observer()->OnSurfaceCreated(surface_id1, gfx::Size(1, 1), 1.0f);
surface_observer()->OnSurfaceCreated(surface_id2, gfx::Size(1, 1), 1.0f);
RunUntilIdle();
// Client should get OnSurfaceCreated call and temporary reference added for
// both surfaces.
EXPECT_EQ("OnSurfaceCreated(2:1:1);OnSurfaceCreated(2:1:2)",
client_.events());
EXPECT_EQ("Add(0:0:0-2:1:1);Add(0:0:0-2:1:2)", reference_manager_.events());
EXPECT_EQ(2u, CountTempReferences());
// Add a reference to the surface with the later LocalFrameId.
AddSurfaceReference(parent_id, surface_id2);
RunUntilIdle();
// The real reference should be added for 2:1:2 and both temporary references
// should be removed.
EXPECT_EQ("Add(1:1:1-2:1:2);Remove(0:0:0-2:1:2);Remove(0:0:0-2:1:1)",
reference_manager_.events());
EXPECT_EQ(0u, CountTempReferences());
}
} // namespace test
} // namespace ui