blob: af7633768fbca2446b8911abaa736b0b2f1fbbd4 [file] [log] [blame]
// Copyright 2017 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/service/display/gl_renderer_copier.h"
#include <stdint.h>
#include <iterator>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/test/test_context_provider.h"
#include "components/viz/test/test_gles2_interface.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/geometry/vector2d.h"
namespace viz {
namespace {
class CopierTestGLES2Interface : public TestGLES2Interface {
public:
// Sets how GL will respond to queries regarding the implementation's internal
// read-back format.
void SetOptimalReadbackFormat(GLenum format, GLenum type) {
format_ = format;
type_ = type;
}
// GLES2Interface override.
void GetIntegerv(GLenum pname, GLint* params) override {
switch (pname) {
case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
ASSERT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
CheckFramebufferStatus(GL_FRAMEBUFFER));
params[0] = format_;
break;
case GL_IMPLEMENTATION_COLOR_READ_TYPE:
ASSERT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
CheckFramebufferStatus(GL_FRAMEBUFFER));
params[0] = type_;
break;
default:
TestGLES2Interface::GetIntegerv(pname, params);
break;
}
}
private:
GLenum format_ = 0;
GLenum type_ = 0;
};
} // namespace
class GLRendererCopierTest : public testing::Test {
public:
using ReusableThings = GLRendererCopier::ReusableThings;
void SetUp() override {
auto context_provider = TestContextProvider::Create(
std::make_unique<CopierTestGLES2Interface>());
context_provider->BindToCurrentThread();
copier_ = std::make_unique<GLRendererCopier>(
std::move(context_provider), nullptr,
base::BindRepeating([](const gfx::Rect& rect) { return rect; }));
}
void TearDown() override { copier_.reset(); }
GLRendererCopier* copier() const { return copier_.get(); }
CopierTestGLES2Interface* test_gl() const {
return static_cast<CopierTestGLES2Interface*>(
copier_->context_provider_->ContextGL());
}
// These simply forward method calls to GLRendererCopier.
std::unique_ptr<ReusableThings> TakeReusableThingsOrCreate(
const base::UnguessableToken& requester) {
return copier_->TakeReusableThingsOrCreate(requester);
}
void StashReusableThingsOrDelete(const base::UnguessableToken& requester,
std::unique_ptr<ReusableThings> things) {
return copier_->StashReusableThingsOrDelete(requester, std::move(things));
}
ReusableThings* PeekReusableThings(const base::UnguessableToken& requester) {
const auto it = copier_->cache_.find(requester);
if (it == copier_->cache_.end())
return nullptr;
return it->second.get();
}
size_t GetCopierCacheSize() { return copier_->cache_.size(); }
void FreeUnusedCachedResources() { copier_->FreeUnusedCachedResources(); }
GLenum GetOptimalReadbackFormat() const {
return copier_->GetOptimalReadbackFormat();
}
static constexpr int kKeepalivePeriod = GLRendererCopier::kKeepalivePeriod;
private:
std::unique_ptr<GLRendererCopier> copier_;
};
// Tests that named objects, such as textures or framebuffers, are only cached
// when the CopyOutputRequest has specified a "source" of requests.
TEST_F(GLRendererCopierTest, ReusesThingsFromSameSource) {
// With no source set in a copy request, expect to never re-use any textures
// or framebuffers.
const base::UnguessableToken no_source;
EXPECT_EQ(0u, GetCopierCacheSize());
auto things = TakeReusableThingsOrCreate(no_source);
EXPECT_TRUE(!!things);
StashReusableThingsOrDelete(no_source, std::move(things));
EXPECT_EQ(nullptr, PeekReusableThings(no_source));
EXPECT_EQ(0u, GetCopierCacheSize());
// With a source set in the request, objects should now be cached and re-used.
const auto source = base::UnguessableToken::Create();
things = TakeReusableThingsOrCreate(source);
ReusableThings* things_raw_ptr = things.get();
EXPECT_TRUE(!!things_raw_ptr);
StashReusableThingsOrDelete(source, std::move(things));
EXPECT_EQ(things_raw_ptr, PeekReusableThings(source));
EXPECT_EQ(1u, GetCopierCacheSize());
// A second, separate source gets its own cache entry.
const auto source2 = base::UnguessableToken::Create();
things = TakeReusableThingsOrCreate(source2);
things_raw_ptr = things.get();
EXPECT_TRUE(!!things_raw_ptr);
EXPECT_EQ(1u, GetCopierCacheSize());
StashReusableThingsOrDelete(source2, std::move(things));
EXPECT_EQ(things_raw_ptr, PeekReusableThings(source2));
EXPECT_EQ(2u, GetCopierCacheSize());
}
// Tests that cached resources are freed if unused for a while.
TEST_F(GLRendererCopierTest, FreesUnusedResources) {
// Take and then stash a ReusableThings instance for a valid source.
const base::UnguessableToken source = base::UnguessableToken::Create();
EXPECT_EQ(0u, GetCopierCacheSize());
StashReusableThingsOrDelete(source, TakeReusableThingsOrCreate(source));
EXPECT_TRUE(!!PeekReusableThings(source));
EXPECT_EQ(1u, GetCopierCacheSize());
// Call FreesUnusedCachedResources() the maximum number of times before the
// cache entry would be considered for freeing.
for (int i = 0; i < kKeepalivePeriod - 1; ++i) {
FreeUnusedCachedResources();
EXPECT_TRUE(!!PeekReusableThings(source));
EXPECT_EQ(1u, GetCopierCacheSize());
if (HasFailure())
break;
}
// Calling FreeUnusedCachedResources() just one more time should cause the
// cache entry to be freed.
FreeUnusedCachedResources();
EXPECT_FALSE(!!PeekReusableThings(source));
EXPECT_EQ(0u, GetCopierCacheSize());
}
TEST_F(GLRendererCopierTest, DetectsBGRAForReadbackFormat) {
test_gl()->SetOptimalReadbackFormat(GL_BGRA_EXT, GL_UNSIGNED_BYTE);
EXPECT_EQ(static_cast<GLenum>(GL_BGRA_EXT), GetOptimalReadbackFormat());
}
TEST_F(GLRendererCopierTest, DetectsRGBAForReadbackFormat) {
test_gl()->SetOptimalReadbackFormat(GL_RGBA, GL_UNSIGNED_BYTE);
EXPECT_EQ(static_cast<GLenum>(GL_RGBA), GetOptimalReadbackFormat());
}
TEST_F(GLRendererCopierTest, FallsBackOnRGBAForReadbackFormat_BadFormat) {
test_gl()->SetOptimalReadbackFormat(GL_RGB, GL_UNSIGNED_BYTE);
EXPECT_EQ(static_cast<GLenum>(GL_RGBA), GetOptimalReadbackFormat());
}
TEST_F(GLRendererCopierTest, FallsBackOnRGBAForReadbackFormat_BadType) {
test_gl()->SetOptimalReadbackFormat(GL_BGRA_EXT, GL_UNSIGNED_SHORT);
EXPECT_EQ(static_cast<GLenum>(GL_RGBA), GetOptimalReadbackFormat());
}
} // namespace viz