blob: 4541458cec90fb4fd07dfe14c6f18998e75fcba9 [file] [log] [blame]
// Copyright 2012 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 "cc/resources/display_resource_provider.h"
#include "cc/resources/layer_tree_resource_provider.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <map>
#include <memory>
#include <set>
#include <unordered_map>
#include <vector>
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "cc/test/render_pass_test_utils.h"
#include "cc/test/resource_provider_test_utils.h"
#include "cc/test/test_context_provider.h"
#include "cc/test/test_shared_bitmap_manager.h"
#include "cc/test/test_texture.h"
#include "cc/test/test_web_graphics_context_3d.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/common/resources/shared_bitmap_manager.h"
#include "components/viz/common/resources/single_release_callback.h"
#include "components/viz/test/test_gpu_memory_buffer_manager.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/gpu_memory_buffer.h"
using testing::InSequence;
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::StrictMock;
using testing::_;
using testing::AnyNumber;
namespace cc {
namespace {
static const bool kUseGpuMemoryBufferResources = false;
static const bool kDelegatedSyncPointsRequired = true;
MATCHER_P(MatchesSyncToken, sync_token, "") {
gpu::SyncToken other;
memcpy(&other, arg, sizeof(other));
return other == sync_token;
}
static void EmptyReleaseCallback(const gpu::SyncToken& sync_token,
bool lost_resource) {}
static void ReleaseCallback(gpu::SyncToken* release_sync_token,
bool* release_lost_resource,
const gpu::SyncToken& sync_token,
bool lost_resource) {
*release_sync_token = sync_token;
*release_lost_resource = lost_resource;
}
static void ReleaseSharedBitmapCallback(
std::unique_ptr<viz::SharedBitmap> shared_bitmap,
bool* release_called,
gpu::SyncToken* release_sync_token,
bool* lost_resource_result,
const gpu::SyncToken& sync_token,
bool lost_resource) {
*release_called = true;
*release_sync_token = sync_token;
*lost_resource_result = lost_resource;
}
static std::unique_ptr<viz::SharedBitmap> CreateAndFillSharedBitmap(
viz::SharedBitmapManager* manager,
const gfx::Size& size,
uint32_t value) {
std::unique_ptr<viz::SharedBitmap> shared_bitmap =
manager->AllocateSharedBitmap(size);
CHECK(shared_bitmap);
uint32_t* pixels = reinterpret_cast<uint32_t*>(shared_bitmap->pixels());
CHECK(pixels);
std::fill_n(pixels, size.GetArea(), value);
return shared_bitmap;
}
static viz::ResourceSettings CreateResourceSettings(
size_t texture_id_allocation_chunk_size = 1) {
viz::ResourceSettings resource_settings;
resource_settings.texture_id_allocation_chunk_size =
texture_id_allocation_chunk_size;
resource_settings.use_gpu_memory_buffer_resources =
kUseGpuMemoryBufferResources;
return resource_settings;
}
class TextureStateTrackingContext : public TestWebGraphicsContext3D {
public:
MOCK_METHOD2(bindTexture, void(GLenum target, GLuint texture));
MOCK_METHOD3(texParameteri, void(GLenum target, GLenum pname, GLint param));
MOCK_METHOD1(waitSyncToken, void(const GLbyte* sync_token));
MOCK_METHOD2(produceTextureDirectCHROMIUM,
void(GLuint texture, const GLbyte* mailbox));
MOCK_METHOD1(createAndConsumeTextureCHROMIUM,
unsigned(const GLbyte* mailbox));
// Force all textures to be consecutive numbers starting at "1",
// so we easily can test for them.
GLuint NextTextureId() override {
base::AutoLock lock(namespace_->lock);
return namespace_->next_texture_id++;
}
void RetireTextureId(GLuint) override {}
void genSyncToken(GLbyte* sync_token) override {
gpu::SyncToken sync_token_data(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x123),
next_fence_sync_++);
sync_token_data.SetVerifyFlush();
memcpy(sync_token, &sync_token_data, sizeof(sync_token_data));
}
GLuint64 GetNextFenceSync() const { return next_fence_sync_; }
GLuint64 next_fence_sync_ = 1;
};
// Shared data between multiple ResourceProviderContext. This contains mailbox
// contents as well as information about sync points.
class ContextSharedData {
public:
static std::unique_ptr<ContextSharedData> Create() {
return base::WrapUnique(new ContextSharedData());
}
uint32_t InsertFenceSync() { return next_fence_sync_++; }
void GenMailbox(GLbyte* mailbox) {
memset(mailbox, 0, GL_MAILBOX_SIZE_CHROMIUM);
memcpy(mailbox, &next_mailbox_, sizeof(next_mailbox_));
++next_mailbox_;
}
void ProduceTexture(const GLbyte* mailbox_name,
const gpu::SyncToken& sync_token,
scoped_refptr<TestTexture> texture) {
uint32_t sync_point = static_cast<uint32_t>(sync_token.release_count());
unsigned mailbox = 0;
memcpy(&mailbox, mailbox_name, sizeof(mailbox));
ASSERT_TRUE(mailbox && mailbox < next_mailbox_);
textures_[mailbox] = texture;
ASSERT_LT(sync_point_for_mailbox_[mailbox], sync_point);
sync_point_for_mailbox_[mailbox] = sync_point;
}
scoped_refptr<TestTexture> ConsumeTexture(const GLbyte* mailbox_name,
const gpu::SyncToken& sync_token) {
unsigned mailbox = 0;
memcpy(&mailbox, mailbox_name, sizeof(mailbox));
DCHECK(mailbox && mailbox < next_mailbox_);
// If the latest sync point the context has waited on is before the sync
// point for when the mailbox was set, pretend we never saw that
// ProduceTexture.
if (sync_point_for_mailbox_[mailbox] > sync_token.release_count()) {
NOTREACHED();
return scoped_refptr<TestTexture>();
}
return textures_[mailbox];
}
private:
ContextSharedData() : next_fence_sync_(1), next_mailbox_(1) {}
uint64_t next_fence_sync_;
unsigned next_mailbox_;
using TextureMap = std::unordered_map<unsigned, scoped_refptr<TestTexture>>;
TextureMap textures_;
std::unordered_map<unsigned, uint32_t> sync_point_for_mailbox_;
};
class ResourceProviderContext : public TestWebGraphicsContext3D {
public:
static std::unique_ptr<ResourceProviderContext> Create(
ContextSharedData* shared_data) {
return base::WrapUnique(new ResourceProviderContext(shared_data));
}
void genSyncToken(GLbyte* sync_token) override {
uint64_t fence_sync = shared_data_->InsertFenceSync();
gpu::SyncToken sync_token_data(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x123),
fence_sync);
sync_token_data.SetVerifyFlush();
// Commit the ProduceTextureDirectCHROMIUM calls at this point, so that
// they're associated with the sync point.
for (const std::unique_ptr<PendingProduceTexture>& pending_texture :
pending_produce_textures_) {
shared_data_->ProduceTexture(pending_texture->mailbox, sync_token_data,
pending_texture->texture);
}
pending_produce_textures_.clear();
memcpy(sync_token, &sync_token_data, sizeof(sync_token_data));
}
void waitSyncToken(const GLbyte* sync_token) override {
gpu::SyncToken sync_token_data;
if (sync_token)
memcpy(&sync_token_data, sync_token, sizeof(sync_token_data));
if (sync_token_data.release_count() >
last_waited_sync_token_.release_count()) {
last_waited_sync_token_ = sync_token_data;
}
}
const gpu::SyncToken& last_waited_sync_token() const {
return last_waited_sync_token_;
}
void texStorage2DEXT(GLenum target,
GLint levels,
GLuint internalformat,
GLint width,
GLint height) override {
CheckTextureIsBound(target);
ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
ASSERT_EQ(1, levels);
GLenum format = GL_RGBA;
switch (internalformat) {
case GL_RGBA8_OES:
break;
case GL_BGRA8_EXT:
format = GL_BGRA_EXT;
break;
default:
NOTREACHED();
}
AllocateTexture(gfx::Size(width, height), format);
}
void texImage2D(GLenum target,
GLint level,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const void* pixels) override {
CheckTextureIsBound(target);
ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
ASSERT_FALSE(level);
ASSERT_EQ(internalformat, format);
ASSERT_FALSE(border);
ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
AllocateTexture(gfx::Size(width, height), format);
if (pixels)
SetPixels(0, 0, width, height, pixels);
}
void texSubImage2D(GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
const void* pixels) override {
CheckTextureIsBound(target);
ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
ASSERT_FALSE(level);
ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
{
base::AutoLock lock_for_texture_access(namespace_->lock);
ASSERT_EQ(GLDataFormat(BoundTexture(target)->format), format);
}
ASSERT_TRUE(pixels);
SetPixels(xoffset, yoffset, width, height, pixels);
}
void genMailboxCHROMIUM(GLbyte* mailbox) override {
return shared_data_->GenMailbox(mailbox);
}
void produceTextureDirectCHROMIUM(GLuint texture,
const GLbyte* mailbox) override {
// Delay moving the texture into the mailbox until the next
// sync token, so that it is not visible to other contexts that
// haven't waited on that sync point.
std::unique_ptr<PendingProduceTexture> pending(new PendingProduceTexture);
memcpy(pending->mailbox, mailbox, sizeof(pending->mailbox));
base::AutoLock lock_for_texture_access(namespace_->lock);
pending->texture = UnboundTexture(texture);
pending_produce_textures_.push_back(std::move(pending));
}
GLuint createAndConsumeTextureCHROMIUM(const GLbyte* mailbox) override {
GLuint texture_id = createTexture();
base::AutoLock lock_for_texture_access(namespace_->lock);
scoped_refptr<TestTexture> texture =
shared_data_->ConsumeTexture(mailbox, last_waited_sync_token_);
namespace_->textures.Replace(texture_id, texture);
return texture_id;
}
void GetPixels(const gfx::Size& size,
viz::ResourceFormat format,
uint8_t* pixels) {
CheckTextureIsBound(GL_TEXTURE_2D);
base::AutoLock lock_for_texture_access(namespace_->lock);
scoped_refptr<TestTexture> texture = BoundTexture(GL_TEXTURE_2D);
ASSERT_EQ(texture->size, size);
ASSERT_EQ(texture->format, format);
memcpy(pixels, texture->data.get(), TextureSizeBytes(size, format));
}
protected:
explicit ResourceProviderContext(ContextSharedData* shared_data)
: shared_data_(shared_data) {}
private:
void AllocateTexture(const gfx::Size& size, GLenum format) {
CheckTextureIsBound(GL_TEXTURE_2D);
viz::ResourceFormat texture_format = viz::RGBA_8888;
switch (format) {
case GL_RGBA:
texture_format = viz::RGBA_8888;
break;
case GL_BGRA_EXT:
texture_format = viz::BGRA_8888;
break;
}
base::AutoLock lock_for_texture_access(namespace_->lock);
BoundTexture(GL_TEXTURE_2D)->Reallocate(size, texture_format);
}
void SetPixels(int xoffset,
int yoffset,
int width,
int height,
const void* pixels) {
CheckTextureIsBound(GL_TEXTURE_2D);
base::AutoLock lock_for_texture_access(namespace_->lock);
scoped_refptr<TestTexture> texture = BoundTexture(GL_TEXTURE_2D);
ASSERT_TRUE(texture->data.get());
ASSERT_TRUE(xoffset >= 0 && xoffset + width <= texture->size.width());
ASSERT_TRUE(yoffset >= 0 && yoffset + height <= texture->size.height());
ASSERT_TRUE(pixels);
size_t in_pitch = TextureSizeBytes(gfx::Size(width, 1), texture->format);
size_t out_pitch =
TextureSizeBytes(gfx::Size(texture->size.width(), 1), texture->format);
uint8_t* dest = texture->data.get() + yoffset * out_pitch +
TextureSizeBytes(gfx::Size(xoffset, 1), texture->format);
const uint8_t* src = static_cast<const uint8_t*>(pixels);
for (int i = 0; i < height; ++i) {
memcpy(dest, src, in_pitch);
dest += out_pitch;
src += in_pitch;
}
}
struct PendingProduceTexture {
GLbyte mailbox[GL_MAILBOX_SIZE_CHROMIUM];
scoped_refptr<TestTexture> texture;
};
ContextSharedData* shared_data_;
gpu::SyncToken last_waited_sync_token_;
std::vector<std::unique_ptr<PendingProduceTexture>> pending_produce_textures_;
};
void GetResourcePixels(DisplayResourceProvider* resource_provider,
ResourceProviderContext* context,
viz::ResourceId id,
const gfx::Size& size,
viz::ResourceFormat format,
uint8_t* pixels) {
resource_provider->WaitSyncToken(id);
if (resource_provider->IsSoftware()) {
DisplayResourceProvider::ScopedReadLockSoftware lock_software(
resource_provider, id);
memcpy(pixels, lock_software.sk_bitmap()->getPixels(),
lock_software.sk_bitmap()->computeByteSize());
} else {
DisplayResourceProvider::ScopedReadLockGL lock_gl(resource_provider, id);
ASSERT_NE(0U, lock_gl.texture_id());
context->bindTexture(GL_TEXTURE_2D, lock_gl.texture_id());
context->GetPixels(size, format, pixels);
}
}
class ResourceProviderTest : public testing::TestWithParam<bool> {
public:
explicit ResourceProviderTest(bool child_needs_sync_token)
: use_gpu_(GetParam()),
child_needs_sync_token_(child_needs_sync_token),
shared_data_(ContextSharedData::Create()) {
if (use_gpu_) {
auto context3d(ResourceProviderContext::Create(shared_data_.get()));
context3d_ = context3d.get();
context_provider_ = TestContextProvider::Create(std::move(context3d));
context_provider_->UnboundTestContext3d()
->set_support_texture_format_bgra8888(true);
context_provider_->BindToCurrentThread();
auto child_context_owned =
ResourceProviderContext::Create(shared_data_.get());
child_context_ = child_context_owned.get();
child_context_provider_ =
TestContextProvider::Create(std::move(child_context_owned));
child_context_provider_->UnboundTestContext3d()
->set_support_texture_format_bgra8888(true);
child_context_provider_->BindToCurrentThread();
gpu_memory_buffer_manager_ =
std::make_unique<viz::TestGpuMemoryBufferManager>();
child_gpu_memory_buffer_manager_ =
gpu_memory_buffer_manager_->CreateClientGpuMemoryBufferManager();
} else {
shared_bitmap_manager_ = std::make_unique<TestSharedBitmapManager>();
}
resource_provider_ = std::make_unique<DisplayResourceProvider>(
context_provider_.get(), shared_bitmap_manager_.get());
MakeChildResourceProvider();
}
ResourceProviderTest() : ResourceProviderTest(true) {}
bool use_gpu() const { return use_gpu_; }
void MakeChildResourceProvider() {
child_resource_provider_ = std::make_unique<LayerTreeResourceProvider>(
child_context_provider_.get(), shared_bitmap_manager_.get(),
child_gpu_memory_buffer_manager_.get(), child_needs_sync_token_,
CreateResourceSettings());
}
static void CollectResources(
std::vector<viz::ReturnedResource>* array,
const std::vector<viz::ReturnedResource>& returned) {
array->insert(array->end(), returned.begin(), returned.end());
}
static ReturnCallback GetReturnCallback(
std::vector<viz::ReturnedResource>* array) {
return base::Bind(&ResourceProviderTest::CollectResources, array);
}
static void SetResourceFilter(DisplayResourceProvider* resource_provider,
viz::ResourceId id,
GLenum filter) {
DisplayResourceProvider::ScopedSamplerGL sampler(resource_provider, id,
GL_TEXTURE_2D, filter);
}
ResourceProviderContext* context() { return context3d_; }
viz::ResourceId CreateChildMailbox(gpu::SyncToken* release_sync_token,
bool* lost_resource,
bool* release_called,
gpu::SyncToken* sync_token) {
if (use_gpu()) {
unsigned texture = child_context_->createTexture();
gpu::Mailbox gpu_mailbox;
child_context_->genMailboxCHROMIUM(gpu_mailbox.name);
child_context_->produceTextureDirectCHROMIUM(texture, gpu_mailbox.name);
child_context_->genSyncToken(sync_token->GetData());
EXPECT_TRUE(sync_token->HasData());
std::unique_ptr<viz::SharedBitmap> shared_bitmap;
std::unique_ptr<viz::SingleReleaseCallback> callback =
viz::SingleReleaseCallback::Create(base::Bind(
ReleaseSharedBitmapCallback, base::Passed(&shared_bitmap),
release_called, release_sync_token, lost_resource));
return child_resource_provider_->ImportResource(
viz::TransferableResource::MakeGL(gpu_mailbox, GL_LINEAR,
GL_TEXTURE_2D, *sync_token),
std::move(callback));
} else {
gfx::Size size(64, 64);
std::unique_ptr<viz::SharedBitmap> shared_bitmap(
CreateAndFillSharedBitmap(shared_bitmap_manager_.get(), size, 0));
viz::SharedBitmap* shared_bitmap_ptr = shared_bitmap.get();
std::unique_ptr<viz::SingleReleaseCallback> callback =
viz::SingleReleaseCallback::Create(base::Bind(
ReleaseSharedBitmapCallback, base::Passed(&shared_bitmap),
release_called, release_sync_token, lost_resource));
return child_resource_provider_->ImportResource(
viz::TransferableResource::MakeSoftware(
shared_bitmap_ptr->id(), shared_bitmap_ptr->sequence_number(),
size),
std::move(callback));
}
}
viz::ResourceId MakeGpuResourceAndSendToDisplay(
char c,
GLuint filter,
GLuint target,
const gpu::SyncToken& sync_token,
DisplayResourceProvider* resource_provider) {
ReturnCallback return_callback =
base::Bind([](const std::vector<viz::ReturnedResource>&) {});
int child = resource_provider->CreateChild(return_callback);
gpu::Mailbox gpu_mailbox;
gpu_mailbox.name[0] = c;
gpu_mailbox.name[1] = 0;
auto resource = viz::TransferableResource::MakeGL(gpu_mailbox, GL_LINEAR,
target, sync_token);
resource.id = 11;
resource_provider->ReceiveFromChild(child, {resource});
auto& map = resource_provider->GetChildToParentMap(child);
return map.find(resource.id)->second;
}
protected:
const bool use_gpu_;
const bool child_needs_sync_token_;
const std::unique_ptr<ContextSharedData> shared_data_;
ResourceProviderContext* context3d_ = nullptr;
ResourceProviderContext* child_context_ = nullptr;
scoped_refptr<TestContextProvider> context_provider_;
scoped_refptr<TestContextProvider> child_context_provider_;
std::unique_ptr<viz::TestGpuMemoryBufferManager> gpu_memory_buffer_manager_;
std::unique_ptr<DisplayResourceProvider> resource_provider_;
std::unique_ptr<viz::TestGpuMemoryBufferManager>
child_gpu_memory_buffer_manager_;
std::unique_ptr<LayerTreeResourceProvider> child_resource_provider_;
std::unique_ptr<TestSharedBitmapManager> shared_bitmap_manager_;
};
TEST_P(ResourceProviderTest, Basic) {
DCHECK_EQ(resource_provider_->IsSoftware(), !use_gpu());
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
size_t pixel_size = TextureSizeBytes(size, format);
ASSERT_EQ(4U, pixel_size);
viz::ResourceId id;
if (!use_gpu()) {
id =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
} else {
id = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
}
EXPECT_EQ(1, static_cast<int>(child_resource_provider_->num_resources()));
if (use_gpu())
EXPECT_EQ(0u, context()->NumTextures());
uint8_t data[4] = {1, 2, 3, 4};
child_resource_provider_->CopyToResource(id, data, size);
if (use_gpu())
EXPECT_EQ(1u, context()->NumTextures());
// Give the resource to the parent to read its pixels.
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent({id}, &list);
resource_provider_->ReceiveFromChild(child_id, list);
auto id_map = resource_provider_->GetChildToParentMap(child_id);
uint8_t result[4] = {0};
GetResourcePixels(resource_provider_.get(), context(), id_map[id], size,
format, result);
EXPECT_EQ(0, memcmp(data, result, pixel_size));
// Give it back to the child.
resource_provider_->DeclareUsedResourcesFromChild(child_id, {});
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
child_resource_provider_->DeleteResource(id);
EXPECT_EQ(0, static_cast<int>(child_resource_provider_->num_resources()));
if (use_gpu())
EXPECT_EQ(0u, context()->NumTextures());
}
TEST_P(ResourceProviderTest, SimpleUpload) {
gfx::Size size(2, 2);
viz::ResourceFormat format = viz::RGBA_8888;
size_t pixel_size = TextureSizeBytes(size, format);
ASSERT_EQ(16U, pixel_size);
viz::ResourceId id1;
viz::ResourceId id2;
if (use_gpu()) {
id1 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
id2 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
} else {
id1 =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
id2 =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
}
uint8_t image[16] = {0};
child_resource_provider_->CopyToResource(id1, image, size);
for (uint8_t i = 0; i < pixel_size; ++i)
image[i] = i;
child_resource_provider_->CopyToResource(id2, image, size);
// Return the mapped resource id.
ResourceProvider::ResourceIdMap resource_map =
SendResourceAndGetChildToParentMap({id1, id2}, resource_provider_.get(),
child_resource_provider_.get());
viz::ResourceId mapped_id1 = resource_map[id1];
viz::ResourceId mapped_id2 = resource_map[id2];
{
uint8_t result[16] = {0};
uint8_t expected[16] = {0};
GetResourcePixels(resource_provider_.get(), context(), mapped_id1, size,
format, result);
EXPECT_EQ(0, memcmp(expected, result, pixel_size));
}
{
uint8_t result[16] = {0};
uint8_t expected[16] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
GetResourcePixels(resource_provider_.get(), context(), mapped_id2, size,
format, result);
EXPECT_EQ(0, memcmp(expected, result, pixel_size));
}
}
TEST_P(ResourceProviderTest, TransferGLResources) {
if (!use_gpu())
return;
gfx::Size size(1, 1);
// Use some non-default format to detect when defaults aren't changed
// correctly.
viz::ResourceFormat format = viz::BGRA_8888;
size_t pixel_size = TextureSizeBytes(size, format);
ASSERT_EQ(4U, pixel_size);
gfx::ColorSpace color_space1 = gfx::ColorSpace::CreateSRGB();
viz::ResourceId id1 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, color_space1);
uint8_t data1[4] = { 1, 2, 3, 4 };
child_resource_provider_->CopyToResource(id1, data1, size);
viz::ResourceId id2 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
uint8_t data2[4] = { 5, 5, 5, 5 };
child_resource_provider_->CopyToResource(id2, data2, size);
viz::ResourceId id3 = child_resource_provider_->CreateGpuMemoryBufferResource(
size, viz::ResourceTextureHint::kDefault, format,
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, gfx::ColorSpace());
{
LayerTreeResourceProvider::ScopedWriteLockGpuMemoryBuffer lock(
child_resource_provider_.get(), id3);
EXPECT_TRUE(lock.GetGpuMemoryBuffer());
}
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
// Transfer some resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
resource_ids_to_transfer.push_back(id3);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(3u, list.size());
EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
EXPECT_TRUE(list[1].mailbox_holder.sync_token.HasData());
EXPECT_EQ(list[0].mailbox_holder.sync_token,
list[1].mailbox_holder.sync_token);
EXPECT_TRUE(list[2].mailbox_holder.sync_token.HasData());
EXPECT_EQ(list[0].mailbox_holder.sync_token,
list[2].mailbox_holder.sync_token);
EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
list[0].mailbox_holder.texture_target);
EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
list[1].mailbox_holder.texture_target);
EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
list[2].mailbox_holder.texture_target);
EXPECT_EQ(list[0].buffer_format, gfx::BufferFormat::RGBA_8888);
EXPECT_EQ(list[1].buffer_format, gfx::BufferFormat::RGBA_8888);
EXPECT_EQ(list[2].buffer_format, gfx::BufferFormat::BGRA_8888);
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id3));
resource_provider_->ReceiveFromChild(child_id, list);
EXPECT_NE(list[0].mailbox_holder.sync_token,
context3d_->last_waited_sync_token());
// In DisplayResourceProvider's namespace, use the mapped resource id.
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
{
viz::ResourceId mapped_resource_id = resource_map[list[0].id];
resource_provider_->WaitSyncToken(mapped_resource_id);
DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
mapped_resource_id);
}
EXPECT_EQ(list[0].mailbox_holder.sync_token,
context3d_->last_waited_sync_token());
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id1);
resource_ids_to_receive.insert(id2);
resource_ids_to_receive.insert(id3);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
resource_ids_to_receive);
EXPECT_EQ(3u, resource_provider_->num_resources());
viz::ResourceId mapped_id1 = resource_map[id1];
viz::ResourceId mapped_id2 = resource_map[id2];
viz::ResourceId mapped_id3 = resource_map[id3];
EXPECT_NE(0u, mapped_id1);
EXPECT_NE(0u, mapped_id2);
EXPECT_NE(0u, mapped_id3);
EXPECT_FALSE(resource_provider_->InUse(mapped_id1));
EXPECT_FALSE(resource_provider_->InUse(mapped_id2));
EXPECT_FALSE(resource_provider_->InUse(mapped_id3));
uint8_t result[4] = { 0 };
GetResourcePixels(
resource_provider_.get(), context(), mapped_id1, size, format, result);
EXPECT_EQ(0, memcmp(data1, result, pixel_size));
GetResourcePixels(
resource_provider_.get(), context(), mapped_id2, size, format, result);
EXPECT_EQ(0, memcmp(data2, result, pixel_size));
EXPECT_FALSE(resource_provider_->IsOverlayCandidate(mapped_id1));
EXPECT_FALSE(resource_provider_->IsOverlayCandidate(mapped_id2));
EXPECT_TRUE(resource_provider_->IsOverlayCandidate(mapped_id3));
{
resource_provider_->WaitSyncToken(mapped_id1);
DisplayResourceProvider::ScopedReadLockGL lock1(resource_provider_.get(),
mapped_id1);
EXPECT_TRUE(lock1.color_space() == color_space1);
}
{
// Check that transfering again the same resource from the child to the
// parent works.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
resource_ids_to_transfer.push_back(id3);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
EXPECT_EQ(3u, list.size());
EXPECT_EQ(id1, list[0].id);
EXPECT_EQ(id2, list[1].id);
EXPECT_EQ(id3, list[2].id);
EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
list[0].mailbox_holder.texture_target);
EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
list[1].mailbox_holder.texture_target);
EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
list[2].mailbox_holder.texture_target);
EXPECT_EQ(list[0].buffer_format, gfx::BufferFormat::RGBA_8888);
EXPECT_EQ(list[1].buffer_format, gfx::BufferFormat::RGBA_8888);
EXPECT_EQ(list[2].buffer_format, gfx::BufferFormat::BGRA_8888);
std::vector<viz::ReturnedResource> returned =
viz::TransferableResource::ReturnResources(list);
child_resource_provider_->ReceiveReturnsFromParent(returned);
// ids were exported twice, we returned them only once, they should still
// be in-use.
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id3));
}
{
EXPECT_EQ(0u, returned_to_child.size());
// Transfer resources back from the parent to the child. Set no resources as
// being in use.
viz::ResourceIdSet no_resources;
resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
ASSERT_EQ(3u, returned_to_child.size());
EXPECT_TRUE(returned_to_child[0].sync_token.HasData());
EXPECT_TRUE(returned_to_child[1].sync_token.HasData());
EXPECT_TRUE(returned_to_child[2].sync_token.HasData());
EXPECT_FALSE(returned_to_child[0].lost);
EXPECT_FALSE(returned_to_child[1].lost);
EXPECT_FALSE(returned_to_child[2].lost);
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
returned_to_child.clear();
}
EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id2));
EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id3));
{
// ScopedWriteLockGL will wait for the sync token, which is convenient since
// |child_resource_provider_| doesn't expose that.
LayerTreeResourceProvider::ScopedWriteLockGL lock(
child_resource_provider_.get(), id1);
ASSERT_NE(0U, lock.GetTexture());
}
// Ensure copying to resource doesn't fail.
child_resource_provider_->CopyToResource(id2, data2, size);
{
// ScopedWriteLockGL will wait for the sync token.
LayerTreeResourceProvider::ScopedWriteLockGL lock(
child_resource_provider_.get(), id2);
ASSERT_NE(0U, lock.GetTexture());
}
{
// Transfer resources to the parent again.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
resource_ids_to_transfer.push_back(id3);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(3u, list.size());
EXPECT_EQ(id1, list[0].id);
EXPECT_EQ(id2, list[1].id);
EXPECT_EQ(id3, list[2].id);
EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
EXPECT_TRUE(list[1].mailbox_holder.sync_token.HasData());
EXPECT_TRUE(list[2].mailbox_holder.sync_token.HasData());
EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
list[0].mailbox_holder.texture_target);
EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
list[1].mailbox_holder.texture_target);
EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
list[2].mailbox_holder.texture_target);
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id3));
resource_provider_->ReceiveFromChild(child_id, list);
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id1);
resource_ids_to_receive.insert(id2);
resource_ids_to_receive.insert(id3);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
resource_ids_to_receive);
}
EXPECT_EQ(0u, returned_to_child.size());
EXPECT_EQ(3u, resource_provider_->num_resources());
resource_provider_->DestroyChild(child_id);
EXPECT_EQ(0u, resource_provider_->num_resources());
ASSERT_EQ(3u, returned_to_child.size());
EXPECT_TRUE(returned_to_child[0].sync_token.HasData());
EXPECT_TRUE(returned_to_child[1].sync_token.HasData());
EXPECT_TRUE(returned_to_child[2].sync_token.HasData());
EXPECT_FALSE(returned_to_child[0].lost);
EXPECT_FALSE(returned_to_child[1].lost);
EXPECT_FALSE(returned_to_child[2].lost);
}
#if defined(OS_ANDROID)
TEST_P(ResourceProviderTest, OverlayPromotionHint) {
if (!use_gpu())
return;
GLuint external_texture_id = child_context_->createExternalTexture();
gpu::Mailbox external_mailbox;
child_context_->genMailboxCHROMIUM(external_mailbox.name);
child_context_->produceTextureDirectCHROMIUM(external_texture_id,
external_mailbox.name);
gpu::SyncToken external_sync_token;
child_context_->genSyncToken(external_sync_token.GetData());
EXPECT_TRUE(external_sync_token.HasData());
viz::TransferableResource id1_transfer =
viz::TransferableResource::MakeGLOverlay(
external_mailbox, GL_LINEAR, GL_TEXTURE_EXTERNAL_OES,
external_sync_token, gfx::Size(1, 1), true);
id1_transfer.wants_promotion_hint = true;
id1_transfer.is_backed_by_surface_texture = true;
viz::ResourceId id1 = child_resource_provider_->ImportResource(
id1_transfer,
viz::SingleReleaseCallback::Create(base::Bind(&EmptyReleaseCallback)));
viz::TransferableResource id2_transfer =
viz::TransferableResource::MakeGLOverlay(
external_mailbox, GL_LINEAR, GL_TEXTURE_EXTERNAL_OES,
external_sync_token, gfx::Size(1, 1), true);
id2_transfer.wants_promotion_hint = false;
id2_transfer.is_backed_by_surface_texture = false;
viz::ResourceId id2 = child_resource_provider_->ImportResource(
id2_transfer,
viz::SingleReleaseCallback::Create(base::Bind(&EmptyReleaseCallback)));
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
// Transfer some resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(2u, list.size());
resource_provider_->ReceiveFromChild(child_id, list);
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
viz::ResourceId mapped_id1 = resource_map[list[0].id];
viz::ResourceId mapped_id2 = resource_map[list[1].id];
// The promotion hints should not be recorded until after we wait. This is
// because we can't notify them until they're synchronized, and we choose to
// ignore unwaited resources rather than send them a "no" hint. If they end
// up in the request set before we wait, then the attempt to notify them wil;
// DCHECK when we try to lock them for reading in SendPromotionHints.
EXPECT_EQ(0u, resource_provider_->CountPromotionHintRequestsForTesting());
{
resource_provider_->WaitSyncToken(mapped_id1);
DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
mapped_id1);
}
EXPECT_EQ(1u, resource_provider_->CountPromotionHintRequestsForTesting());
EXPECT_EQ(list[0].mailbox_holder.sync_token,
context3d_->last_waited_sync_token());
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id1);
resource_ids_to_receive.insert(id2);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
resource_ids_to_receive);
EXPECT_EQ(2u, resource_provider_->num_resources());
EXPECT_NE(0u, mapped_id1);
EXPECT_NE(0u, mapped_id2);
// Make sure that the request for a promotion hint was noticed.
EXPECT_TRUE(resource_provider_->IsOverlayCandidate(mapped_id1));
EXPECT_TRUE(resource_provider_->IsBackedBySurfaceTexture(mapped_id1));
EXPECT_TRUE(resource_provider_->WantsPromotionHintForTesting(mapped_id1));
EXPECT_TRUE(resource_provider_->IsOverlayCandidate(mapped_id2));
EXPECT_FALSE(resource_provider_->IsBackedBySurfaceTexture(mapped_id2));
EXPECT_FALSE(resource_provider_->WantsPromotionHintForTesting(mapped_id2));
// ResourceProvider maintains a set of promotion hint requests that should be
// cleared when resources are deleted.
resource_provider_->DeclareUsedResourcesFromChild(child_id,
viz::ResourceIdSet());
EXPECT_EQ(2u, returned_to_child.size());
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
EXPECT_EQ(0u, resource_provider_->CountPromotionHintRequestsForTesting());
resource_provider_->DestroyChild(child_id);
}
#endif
TEST_P(ResourceProviderTest, TransferGLResources_NoSyncToken) {
if (!use_gpu())
return;
bool need_sync_tokens = false;
auto no_token_resource_provider = std::make_unique<LayerTreeResourceProvider>(
child_context_provider_.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), need_sync_tokens,
CreateResourceSettings());
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
size_t pixel_size = TextureSizeBytes(size, format);
ASSERT_EQ(4U, pixel_size);
viz::ResourceId id1 = no_token_resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
uint8_t data1[4] = {1, 2, 3, 4};
no_token_resource_provider->CopyToResource(id1, data1, size);
viz::ResourceId id2 =
no_token_resource_provider->CreateGpuMemoryBufferResource(
size, viz::ResourceTextureHint::kDefault, format,
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, gfx::ColorSpace());
{
// Ensure locking the memory buffer doesn't create an unnecessary sync
// point.
LayerTreeResourceProvider::ScopedWriteLockGpuMemoryBuffer lock(
no_token_resource_provider.get(), id2);
EXPECT_TRUE(lock.GetGpuMemoryBuffer());
}
GLuint external_texture_id = child_context_->createExternalTexture();
// A sync point is specified directly and should be used.
gpu::Mailbox external_mailbox;
child_context_->genMailboxCHROMIUM(external_mailbox.name);
child_context_->produceTextureDirectCHROMIUM(external_texture_id,
external_mailbox.name);
gpu::SyncToken external_sync_token;
child_context_->genSyncToken(external_sync_token.GetData());
EXPECT_TRUE(external_sync_token.HasData());
viz::ResourceId id3 = no_token_resource_provider->ImportResource(
viz::TransferableResource::MakeGL(external_mailbox, GL_LINEAR,
GL_TEXTURE_EXTERNAL_OES,
external_sync_token),
viz::SingleReleaseCallback::Create(base::Bind(&EmptyReleaseCallback)));
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
resource_provider_->SetChildNeedsSyncTokens(child_id, false);
{
// Transfer some resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
resource_ids_to_transfer.push_back(id3);
std::vector<viz::TransferableResource> list;
no_token_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(3u, list.size());
// Standard resources shouldn't require creating and sending a sync point.
EXPECT_FALSE(list[0].mailbox_holder.sync_token.HasData());
EXPECT_FALSE(list[1].mailbox_holder.sync_token.HasData());
// A given sync point should be passed through.
EXPECT_EQ(external_sync_token, list[2].mailbox_holder.sync_token);
resource_provider_->ReceiveFromChild(child_id, list);
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id1);
resource_ids_to_receive.insert(id2);
resource_ids_to_receive.insert(id3);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
resource_ids_to_receive);
}
{
EXPECT_EQ(0u, returned_to_child.size());
// Transfer resources back from the parent to the child. Set no resources as
// being in use.
viz::ResourceIdSet no_resources;
resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
ASSERT_EQ(3u, returned_to_child.size());
std::map<viz::ResourceId, gpu::SyncToken> returned_sync_tokens;
for (const auto& returned : returned_to_child)
returned_sync_tokens[returned.id] = returned.sync_token;
ASSERT_TRUE(returned_sync_tokens.find(id1) != returned_sync_tokens.end());
// No new sync point should be created transferring back.
ASSERT_TRUE(returned_sync_tokens.find(id1) != returned_sync_tokens.end());
EXPECT_FALSE(returned_sync_tokens[id1].HasData());
ASSERT_TRUE(returned_sync_tokens.find(id2) != returned_sync_tokens.end());
EXPECT_FALSE(returned_sync_tokens[id2].HasData());
// Original sync point given should be returned.
ASSERT_TRUE(returned_sync_tokens.find(id3) != returned_sync_tokens.end());
EXPECT_EQ(external_sync_token, returned_sync_tokens[id3]);
EXPECT_FALSE(returned_to_child[0].lost);
EXPECT_FALSE(returned_to_child[1].lost);
EXPECT_FALSE(returned_to_child[2].lost);
no_token_resource_provider->ReceiveReturnsFromParent(returned_to_child);
returned_to_child.clear();
}
resource_provider_->DestroyChild(child_id);
}
// Test that SetBatchReturnResources batching works.
TEST_P(ResourceProviderTest, SetBatchPreventsReturn) {
if (!use_gpu())
return;
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
uint8_t data1[4] = {1, 2, 3, 4};
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
// Transfer some resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
viz::ResourceId ids[2];
for (size_t i = 0; i < arraysize(ids); i++) {
ids[i] = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
child_resource_provider_->CopyToResource(ids[i], data1, size);
resource_ids_to_transfer.push_back(ids[i]);
}
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(2u, list.size());
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(ids[0]));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(ids[1]));
resource_provider_->ReceiveFromChild(child_id, list);
// In DisplayResourceProvider's namespace, use the mapped resource id.
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
std::vector<std::unique_ptr<DisplayResourceProvider::ScopedReadLockGL>>
read_locks;
for (auto& resource_id : list) {
unsigned int mapped_resource_id = resource_map[resource_id.id];
resource_provider_->WaitSyncToken(mapped_resource_id);
read_locks.push_back(
std::make_unique<DisplayResourceProvider::ScopedReadLockGL>(
resource_provider_.get(), mapped_resource_id));
}
resource_provider_->DeclareUsedResourcesFromChild(child_id,
viz::ResourceIdSet());
std::unique_ptr<DisplayResourceProvider::ScopedBatchReturnResources>
returner =
std::make_unique<DisplayResourceProvider::ScopedBatchReturnResources>(
resource_provider_.get());
EXPECT_EQ(0u, returned_to_child.size());
read_locks.clear();
EXPECT_EQ(0u, returned_to_child.size());
returner.reset();
EXPECT_EQ(2u, returned_to_child.size());
// All resources in a batch should share a sync token.
EXPECT_EQ(returned_to_child[0].sync_token, returned_to_child[1].sync_token);
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
child_resource_provider_->DeleteResource(ids[0]);
child_resource_provider_->DeleteResource(ids[1]);
EXPECT_EQ(0u, child_resource_provider_->num_resources());
}
TEST_P(ResourceProviderTest, ReadLockCountStopsReturnToChildOrDelete) {
if (!use_gpu())
return;
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
viz::ResourceId id1 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
uint8_t data1[4] = {1, 2, 3, 4};
child_resource_provider_->CopyToResource(id1, data1, size);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
// Transfer some resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(1u, list.size());
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
resource_provider_->ReceiveFromChild(child_id, list);
// In DisplayResourceProvider's namespace, use the mapped resource id.
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
viz::ResourceId mapped_resource_id = resource_map[list[0].id];
resource_provider_->WaitSyncToken(mapped_resource_id);
DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
mapped_resource_id);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
viz::ResourceIdSet());
EXPECT_EQ(0u, returned_to_child.size());
}
EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
// No need to wait for the sync token here -- it will be returned to the
// client on delete.
EXPECT_EQ(1u, child_resource_provider_->num_resources());
child_resource_provider_->DeleteResource(id1);
EXPECT_EQ(0u, child_resource_provider_->num_resources());
resource_provider_->DestroyChild(child_id);
}
class TestFence : public viz::ResourceFence {
public:
TestFence() = default;
// viz::ResourceFence implementation.
void Set() override {}
bool HasPassed() override { return passed; }
void Wait() override {}
bool passed = false;
private:
~TestFence() override = default;
};
TEST_P(ResourceProviderTest, ReadLockFenceStopsReturnToChildOrDelete) {
if (!use_gpu())
return;
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
viz::ResourceId id1 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
uint8_t data1[4] = {1, 2, 3, 4};
child_resource_provider_->CopyToResource(id1, data1, size);
child_resource_provider_->EnableReadLockFencesForTesting(id1);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
// Transfer some resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(1u, list.size());
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_TRUE(list[0].read_lock_fences_enabled);
resource_provider_->ReceiveFromChild(child_id, list);
// In DisplayResourceProvider's namespace, use the mapped resource id.
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
scoped_refptr<TestFence> fence(new TestFence);
resource_provider_->SetReadLockFence(fence.get());
{
unsigned parent_id = resource_map[list.front().id];
resource_provider_->WaitSyncToken(parent_id);
DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
parent_id);
}
resource_provider_->DeclareUsedResourcesFromChild(child_id,
viz::ResourceIdSet());
EXPECT_EQ(0u, returned_to_child.size());
resource_provider_->DeclareUsedResourcesFromChild(child_id,
viz::ResourceIdSet());
EXPECT_EQ(0u, returned_to_child.size());
fence->passed = true;
resource_provider_->DeclareUsedResourcesFromChild(child_id,
viz::ResourceIdSet());
EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
child_resource_provider_->DeleteResource(id1);
EXPECT_EQ(0u, child_resource_provider_->num_resources());
}
TEST_P(ResourceProviderTest, ReadLockFenceDestroyChild) {
if (!use_gpu())
return;
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
viz::ResourceId id1 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
uint8_t data[4] = {1, 2, 3, 4};
child_resource_provider_->CopyToResource(id1, data, size);
child_resource_provider_->EnableReadLockFencesForTesting(id1);
viz::ResourceId id2 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
child_resource_provider_->CopyToResource(id2, data, size);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
// Transfer resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(2u, list.size());
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
resource_provider_->ReceiveFromChild(child_id, list);
// In DisplayResourceProvider's namespace, use the mapped resource id.
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
scoped_refptr<TestFence> fence(new TestFence);
resource_provider_->SetReadLockFence(fence.get());
{
for (size_t i = 0; i < list.size(); i++) {
unsigned parent_id = resource_map[list[i].id];
resource_provider_->WaitSyncToken(parent_id);
DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
parent_id);
}
}
EXPECT_EQ(0u, returned_to_child.size());
EXPECT_EQ(2u, resource_provider_->num_resources());
resource_provider_->DestroyChild(child_id);
EXPECT_EQ(0u, resource_provider_->num_resources());
EXPECT_EQ(2u, returned_to_child.size());
// id1 should be lost and id2 should not.
EXPECT_EQ(returned_to_child[0].lost, returned_to_child[0].id == id1);
EXPECT_EQ(returned_to_child[1].lost, returned_to_child[1].id == id1);
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
child_resource_provider_->DeleteResource(id1);
child_resource_provider_->DeleteResource(id2);
EXPECT_EQ(0u, child_resource_provider_->num_resources());
}
TEST_P(ResourceProviderTest, ReadLockFenceContextLost) {
if (!use_gpu())
return;
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
viz::ResourceId id1 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
uint8_t data[4] = {1, 2, 3, 4};
child_resource_provider_->CopyToResource(id1, data, size);
child_resource_provider_->EnableReadLockFencesForTesting(id1);
viz::ResourceId id2 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
child_resource_provider_->CopyToResource(id2, data, size);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
// Transfer resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(2u, list.size());
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
resource_provider_->ReceiveFromChild(child_id, list);
// In DisplayResourceProvider's namespace, use the mapped resource id.
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
scoped_refptr<TestFence> fence(new TestFence);
resource_provider_->SetReadLockFence(fence.get());
{
for (size_t i = 0; i < list.size(); i++) {
unsigned parent_id = resource_map[list[i].id];
resource_provider_->WaitSyncToken(parent_id);
DisplayResourceProvider::ScopedReadLockGL lock(resource_provider_.get(),
parent_id);
}
}
EXPECT_EQ(0u, returned_to_child.size());
EXPECT_EQ(2u, resource_provider_->num_resources());
resource_provider_->DidLoseContextProvider();
resource_provider_ = nullptr;
EXPECT_EQ(2u, returned_to_child.size());
EXPECT_TRUE(returned_to_child[0].lost);
EXPECT_TRUE(returned_to_child[1].lost);
}
TEST_P(ResourceProviderTest, TransferSoftwareResources) {
if (use_gpu())
return;
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
size_t pixel_size = TextureSizeBytes(size, format);
ASSERT_EQ(4U, pixel_size);
viz::ResourceId id1 =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
uint8_t data1[4] = { 1, 2, 3, 4 };
child_resource_provider_->CopyToResource(id1, data1, size);
viz::ResourceId id2 =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
uint8_t data2[4] = { 5, 5, 5, 5 };
child_resource_provider_->CopyToResource(id2, data2, size);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
// Transfer some resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(2u, list.size());
EXPECT_FALSE(list[0].mailbox_holder.sync_token.HasData());
EXPECT_FALSE(list[1].mailbox_holder.sync_token.HasData());
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
resource_provider_->ReceiveFromChild(child_id, list);
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id1);
resource_ids_to_receive.insert(id2);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
resource_ids_to_receive);
}
EXPECT_EQ(2u, resource_provider_->num_resources());
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
viz::ResourceId mapped_id1 = resource_map[id1];
viz::ResourceId mapped_id2 = resource_map[id2];
EXPECT_NE(0u, mapped_id1);
EXPECT_NE(0u, mapped_id2);
EXPECT_FALSE(resource_provider_->InUse(mapped_id1));
EXPECT_FALSE(resource_provider_->InUse(mapped_id2));
uint8_t result[4] = { 0 };
GetResourcePixels(
resource_provider_.get(), context(), mapped_id1, size, format, result);
EXPECT_EQ(0, memcmp(data1, result, pixel_size));
GetResourcePixels(
resource_provider_.get(), context(), mapped_id2, size, format, result);
EXPECT_EQ(0, memcmp(data2, result, pixel_size));
{
// Check that transfering again the same resource from the child to the
// parent works.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
EXPECT_EQ(2u, list.size());
EXPECT_EQ(id1, list[0].id);
EXPECT_EQ(id2, list[1].id);
std::vector<viz::ReturnedResource> returned =
viz::TransferableResource::ReturnResources(list);
child_resource_provider_->ReceiveReturnsFromParent(returned);
// ids were exported twice, we returned them only once, they should still
// be in-use.
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
}
{
EXPECT_EQ(0u, returned_to_child.size());
// Transfer resources back from the parent to the child. Set no resources as
// being in use.
viz::ResourceIdSet no_resources;
resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
ASSERT_EQ(2u, returned_to_child.size());
EXPECT_FALSE(returned_to_child[0].sync_token.HasData());
EXPECT_FALSE(returned_to_child[1].sync_token.HasData());
std::set<viz::ResourceId> expected_ids;
expected_ids.insert(id1);
expected_ids.insert(id2);
std::set<viz::ResourceId> returned_ids;
for (unsigned i = 0; i < returned_to_child.size(); i++)
returned_ids.insert(returned_to_child[i].id);
EXPECT_EQ(expected_ids, returned_ids);
EXPECT_FALSE(returned_to_child[0].lost);
EXPECT_FALSE(returned_to_child[1].lost);
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
returned_to_child.clear();
}
EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_FALSE(child_resource_provider_->InUseByConsumer(id2));
{
LayerTreeResourceProvider::ScopedWriteLockSoftware lock(
child_resource_provider_.get(), id1);
const SkBitmap sk_bitmap = lock.sk_bitmap();
EXPECT_EQ(sk_bitmap.width(), size.width());
EXPECT_EQ(sk_bitmap.height(), size.height());
EXPECT_EQ(0, memcmp(data1, sk_bitmap.getPixels(), pixel_size));
}
{
LayerTreeResourceProvider::ScopedWriteLockSoftware lock(
child_resource_provider_.get(), id2);
const SkBitmap sk_bitmap = lock.sk_bitmap();
EXPECT_EQ(sk_bitmap.width(), size.width());
EXPECT_EQ(sk_bitmap.height(), size.height());
EXPECT_EQ(0, memcmp(data2, sk_bitmap.getPixels(), pixel_size));
}
{
// Transfer resources to the parent again.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(2u, list.size());
EXPECT_EQ(id1, list[0].id);
EXPECT_EQ(id2, list[1].id);
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
resource_provider_->ReceiveFromChild(child_id, list);
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id1);
resource_ids_to_receive.insert(id2);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
resource_ids_to_receive);
}
EXPECT_EQ(0u, returned_to_child.size());
EXPECT_EQ(2u, resource_provider_->num_resources());
resource_provider_->DestroyChild(child_id);
EXPECT_EQ(0u, resource_provider_->num_resources());
ASSERT_EQ(2u, returned_to_child.size());
EXPECT_FALSE(returned_to_child[0].sync_token.HasData());
EXPECT_FALSE(returned_to_child[1].sync_token.HasData());
std::set<viz::ResourceId> expected_ids;
expected_ids.insert(id1);
expected_ids.insert(id2);
std::set<viz::ResourceId> returned_ids;
for (unsigned i = 0; i < returned_to_child.size(); i++)
returned_ids.insert(returned_to_child[i].id);
EXPECT_EQ(expected_ids, returned_ids);
EXPECT_FALSE(returned_to_child[0].lost);
EXPECT_FALSE(returned_to_child[1].lost);
}
TEST_P(ResourceProviderTest, TransferGLToSoftware) {
if (use_gpu())
return;
scoped_refptr<TestContextProvider> child_context_provider =
TestContextProvider::Create(
ResourceProviderContext::Create(shared_data_.get()));
child_context_provider->BindToCurrentThread();
auto child_resource_provider(std::make_unique<LayerTreeResourceProvider>(
child_context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
size_t pixel_size = TextureSizeBytes(size, format);
ASSERT_EQ(4U, pixel_size);
viz::ResourceId id1 = child_resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, viz::RGBA_8888,
gfx::ColorSpace());
uint8_t data1[4] = { 1, 2, 3, 4 };
child_resource_provider->CopyToResource(id1, data1, size);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
std::vector<viz::TransferableResource> list;
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(1u, list.size());
EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
EXPECT_EQ(static_cast<GLenum>(GL_TEXTURE_2D),
list[0].mailbox_holder.texture_target);
EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1));
resource_provider_->ReceiveFromChild(child_id, list);
}
EXPECT_EQ(0u, resource_provider_->num_resources());
ASSERT_EQ(1u, returned_to_child.size());
EXPECT_EQ(returned_to_child[0].id, id1);
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
viz::ResourceId mapped_id1 = resource_map[id1];
EXPECT_EQ(0u, mapped_id1);
resource_provider_->DestroyChild(child_id);
EXPECT_EQ(0u, resource_provider_->num_resources());
ASSERT_EQ(1u, returned_to_child.size());
EXPECT_FALSE(returned_to_child[0].lost);
}
TEST_P(ResourceProviderTest, TransferInvalidSoftware) {
if (use_gpu())
return;
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
size_t pixel_size = TextureSizeBytes(size, format);
ASSERT_EQ(4U, pixel_size);
viz::ResourceId id1 =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
uint8_t data1[4] = { 1, 2, 3, 4 };
child_resource_provider_->CopyToResource(id1, data1, size);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(1u, list.size());
// Make invalid.
list[0].mailbox_holder.mailbox.name[1] ^= 0xff;
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
resource_provider_->ReceiveFromChild(child_id, list);
}
EXPECT_EQ(1u, resource_provider_->num_resources());
EXPECT_EQ(0u, returned_to_child.size());
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
viz::ResourceId mapped_id1 = resource_map[id1];
EXPECT_NE(0u, mapped_id1);
{
DisplayResourceProvider::ScopedReadLockSoftware lock(
resource_provider_.get(), mapped_id1);
EXPECT_FALSE(lock.valid());
}
resource_provider_->DestroyChild(child_id);
EXPECT_EQ(0u, resource_provider_->num_resources());
ASSERT_EQ(1u, returned_to_child.size());
EXPECT_FALSE(returned_to_child[0].lost);
}
TEST_P(ResourceProviderTest, DeleteExportedResources) {
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
size_t pixel_size = TextureSizeBytes(size, format);
ASSERT_EQ(4U, pixel_size);
viz::ResourceId id1;
viz::ResourceId id2;
if (use_gpu()) {
id1 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
id2 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
} else {
id1 =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
id2 =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
}
uint8_t data1[4] = { 1, 2, 3, 4 };
child_resource_provider_->CopyToResource(id1, data1, size);
uint8_t data2[4] = {5, 5, 5, 5};
child_resource_provider_->CopyToResource(id2, data2, size);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
// Transfer some resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(2u, list.size());
if (use_gpu()) {
EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
EXPECT_TRUE(list[1].mailbox_holder.sync_token.HasData());
}
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
resource_provider_->ReceiveFromChild(child_id, list);
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id1);
resource_ids_to_receive.insert(id2);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
resource_ids_to_receive);
std::vector<viz::ReturnedResource> returned =
viz::TransferableResource::ReturnResources(list);
child_resource_provider_->ReceiveReturnsFromParent(returned);
EXPECT_EQ(0u, returned_to_child.size());
EXPECT_EQ(2u, resource_provider_->num_resources());
// Return the resources from the parent, it should be returned at this
// point.
viz::ResourceIdSet no_resources;
resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
EXPECT_EQ(2u, returned_to_child.size());
EXPECT_EQ(0u, resource_provider_->num_resources());
}
}
TEST_P(ResourceProviderTest, DestroyChildWithExportedResources) {
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
size_t pixel_size = TextureSizeBytes(size, format);
ASSERT_EQ(4U, pixel_size);
viz::ResourceId id1;
viz::ResourceId id2;
if (use_gpu()) {
id1 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
id2 = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
} else {
id1 =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
id2 =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
}
uint8_t data1[4] = {1, 2, 3, 4};
child_resource_provider_->CopyToResource(id1, data1, size);
uint8_t data2[4] = {5, 5, 5, 5};
child_resource_provider_->CopyToResource(id2, data2, size);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
// Transfer some resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
resource_ids_to_transfer.push_back(id2);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(2u, list.size());
if (use_gpu()) {
EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
EXPECT_TRUE(list[1].mailbox_holder.sync_token.HasData());
}
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id2));
resource_provider_->ReceiveFromChild(child_id, list);
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id1);
resource_ids_to_receive.insert(id2);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
resource_ids_to_receive);
EXPECT_EQ(0u, returned_to_child.size());
EXPECT_EQ(2u, resource_provider_->num_resources());
// Destroy the child, the resources should be returned.
resource_provider_->DestroyChild(child_id);
EXPECT_EQ(2u, returned_to_child.size());
EXPECT_EQ(0u, resource_provider_->num_resources());
}
}
TEST_P(ResourceProviderTest, DeleteTransferredResources) {
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
size_t pixel_size = TextureSizeBytes(size, format);
ASSERT_EQ(4U, pixel_size);
viz::ResourceId id;
if (use_gpu()) {
id = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
} else {
id =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
}
uint8_t data[4] = { 1, 2, 3, 4 };
child_resource_provider_->CopyToResource(id, data, size);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
// Transfer some resource to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(1u, list.size());
if (use_gpu())
EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id));
resource_provider_->ReceiveFromChild(child_id, list);
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
resource_ids_to_receive);
}
// Delete textures in the child, while they are transfered.
child_resource_provider_->DeleteResource(id);
EXPECT_EQ(1u, child_resource_provider_->num_resources());
{
EXPECT_EQ(0u, returned_to_child.size());
// Transfer resources back from the parent to the child. Set no resources as
// being in use.
viz::ResourceIdSet no_resources;
resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
ASSERT_EQ(1u, returned_to_child.size());
if (use_gpu())
EXPECT_TRUE(returned_to_child[0].sync_token.HasData());
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
EXPECT_EQ(0u, child_resource_provider_->num_resources());
}
}
class ResourceProviderTestTextureFilters : public ResourceProviderTest {
public:
static void RunTest(GLenum child_filter, GLenum parent_filter) {
auto child_context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* child_context = child_context_owned.get();
auto child_context_provider =
TestContextProvider::Create(std::move(child_context_owned));
child_context_provider->BindToCurrentThread();
auto shared_bitmap_manager = std::make_unique<TestSharedBitmapManager>();
viz::ResourceSettings resource_settings = CreateResourceSettings();
auto child_resource_provider(std::make_unique<LayerTreeResourceProvider>(
child_context_provider.get(), shared_bitmap_manager.get(), nullptr,
kDelegatedSyncPointsRequired, resource_settings));
auto parent_context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* parent_context = parent_context_owned.get();
auto parent_context_provider =
TestContextProvider::Create(std::move(parent_context_owned));
parent_context_provider->BindToCurrentThread();
auto parent_resource_provider(std::make_unique<DisplayResourceProvider>(
parent_context_provider.get(), shared_bitmap_manager.get()));
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
int child_texture_id = 1;
int parent_texture_id = 2;
size_t pixel_size = TextureSizeBytes(size, format);
ASSERT_EQ(4U, pixel_size);
viz::ResourceId id = child_resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
// The new texture is created with GL_LINEAR.
EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, child_texture_id))
.Times(2); // Once to create and once to allocate.
EXPECT_CALL(*child_context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
EXPECT_CALL(*child_context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
EXPECT_CALL(
*child_context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
EXPECT_CALL(
*child_context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
child_resource_provider->AllocateForTesting(id);
Mock::VerifyAndClearExpectations(child_context);
uint8_t data[4] = { 1, 2, 3, 4 };
EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, child_texture_id));
child_resource_provider->CopyToResource(id, data, size);
Mock::VerifyAndClearExpectations(child_context);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id = parent_resource_provider->CreateChild(
GetReturnCallback(&returned_to_child));
// Transfer some resource to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id);
std::vector<viz::TransferableResource> list;
EXPECT_CALL(*child_context, produceTextureDirectCHROMIUM(_, _));
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&list);
Mock::VerifyAndClearExpectations(child_context);
ASSERT_EQ(1u, list.size());
EXPECT_EQ(static_cast<unsigned>(child_filter), list[0].filter);
EXPECT_CALL(*parent_context, createAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(parent_texture_id));
parent_resource_provider->ReceiveFromChild(child_id, list);
ResourceProvider::ResourceIdMap resource_map =
parent_resource_provider->GetChildToParentMap(child_id);
viz::ResourceId mapped_id = resource_map[id];
EXPECT_NE(0u, mapped_id);
{
parent_resource_provider->WaitSyncToken(mapped_id);
DisplayResourceProvider::ScopedReadLockGL lock(
parent_resource_provider.get(), mapped_id);
}
Mock::VerifyAndClearExpectations(parent_context);
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id);
parent_resource_provider->DeclareUsedResourcesFromChild(
child_id, resource_ids_to_receive);
Mock::VerifyAndClearExpectations(parent_context);
// The texture is set to |parent_filter| in the parent.
EXPECT_CALL(*parent_context, bindTexture(GL_TEXTURE_2D, parent_texture_id));
EXPECT_CALL(
*parent_context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, parent_filter));
EXPECT_CALL(
*parent_context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, parent_filter));
SetResourceFilter(parent_resource_provider.get(), mapped_id, parent_filter);
Mock::VerifyAndClearExpectations(parent_context);
// The texture should be reset to |child_filter| in the parent when it is
// returned, since that is how it was received.
EXPECT_CALL(*parent_context, bindTexture(GL_TEXTURE_2D, parent_texture_id));
EXPECT_CALL(*parent_context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
EXPECT_CALL(*parent_context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
{
EXPECT_EQ(0u, returned_to_child.size());
// Transfer resources back from the parent to the child. Set no resources
// as being in use.
viz::ResourceIdSet no_resources;
parent_resource_provider->DeclareUsedResourcesFromChild(child_id,
no_resources);
Mock::VerifyAndClearExpectations(parent_context);
ASSERT_EQ(1u, returned_to_child.size());
child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
}
}
};
TEST_P(ResourceProviderTest, TextureFilters_ChildLinearParentNearest) {
if (!use_gpu())
return;
ResourceProviderTestTextureFilters::RunTest(GL_LINEAR, GL_NEAREST);
}
TEST_P(ResourceProviderTest, TransferMailboxResources) {
// Other mailbox transfers tested elsewhere.
if (!use_gpu())
return;
unsigned texture = context()->createTexture();
context()->bindTexture(GL_TEXTURE_2D, texture);
uint8_t data[4] = { 1, 2, 3, 4 };
context()->texImage2D(
GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &data);
gpu::Mailbox mailbox;
context()->genMailboxCHROMIUM(mailbox.name);
context()->produceTextureDirectCHROMIUM(texture, mailbox.name);
gpu::SyncToken sync_token;
context()->genSyncToken(sync_token.GetData());
EXPECT_TRUE(sync_token.HasData());
// All the logic below assumes that the sync token releases are all positive.
EXPECT_LT(0u, sync_token.release_count());
gpu::SyncToken release_sync_token;
bool lost_resource = false;
viz::ReleaseCallback callback =
base::Bind(ReleaseCallback, &release_sync_token, &lost_resource);
viz::ResourceId resource = child_resource_provider_->ImportResource(
viz::TransferableResource::MakeGL(mailbox, GL_LINEAR, GL_TEXTURE_2D,
sync_token),
viz::SingleReleaseCallback::Create(std::move(callback)));
EXPECT_EQ(1u, context()->NumTextures());
EXPECT_FALSE(release_sync_token.HasData());
{
// Transfer the resource, expect the sync points to be consistent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(resource);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(1u, list.size());
EXPECT_LE(sync_token.release_count(),
list[0].mailbox_holder.sync_token.release_count());
EXPECT_EQ(0,
memcmp(mailbox.name,
list[0].mailbox_holder.mailbox.name,
sizeof(mailbox.name)));
EXPECT_FALSE(release_sync_token.HasData());
context()->waitSyncToken(list[0].mailbox_holder.sync_token.GetConstData());
unsigned other_texture =
context()->createAndConsumeTextureCHROMIUM(mailbox.name);
uint8_t test_data[4] = { 0 };
context()->GetPixels(gfx::Size(1, 1), viz::RGBA_8888, test_data);
EXPECT_EQ(0, memcmp(data, test_data, sizeof(data)));
context()->produceTextureDirectCHROMIUM(other_texture, mailbox.name);
context()->deleteTexture(other_texture);
context()->genSyncToken(list[0].mailbox_holder.sync_token.GetData());
EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
// Receive the resource, then delete it, expect the sync points to be
// consistent.
std::vector<viz::ReturnedResource> returned =
viz::TransferableResource::ReturnResources(list);
child_resource_provider_->ReceiveReturnsFromParent(returned);
EXPECT_EQ(1u, context()->NumTextures());
EXPECT_FALSE(release_sync_token.HasData());
child_resource_provider_->RemoveImportedResource(resource);
EXPECT_LE(list[0].mailbox_holder.sync_token.release_count(),
release_sync_token.release_count());
EXPECT_FALSE(lost_resource);
}
// We're going to do the same thing as above, but testing the case where we
// delete the resource before we receive it back.
sync_token = release_sync_token;
EXPECT_LT(0u, sync_token.release_count());
release_sync_token.Clear();
callback = base::Bind(ReleaseCallback, &release_sync_token, &lost_resource);
resource = child_resource_provider_->ImportResource(
viz::TransferableResource::MakeGL(mailbox, GL_LINEAR, GL_TEXTURE_2D,
sync_token),
viz::SingleReleaseCallback::Create(std::move(callback)));
EXPECT_EQ(1u, context()->NumTextures());
EXPECT_FALSE(release_sync_token.HasData());
{
// Transfer the resource, expect the sync points to be consistent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(resource);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(1u, list.size());
EXPECT_LE(sync_token.release_count(),
list[0].mailbox_holder.sync_token.release_count());
EXPECT_EQ(0,
memcmp(mailbox.name,
list[0].mailbox_holder.mailbox.name,
sizeof(mailbox.name)));
EXPECT_FALSE(release_sync_token.HasData());
context()->waitSyncToken(list[0].mailbox_holder.sync_token.GetConstData());
unsigned other_texture =
context()->createAndConsumeTextureCHROMIUM(mailbox.name);
uint8_t test_data[4] = { 0 };
context()->GetPixels(gfx::Size(1, 1), viz::RGBA_8888, test_data);
EXPECT_EQ(0, memcmp(data, test_data, sizeof(data)));
context()->produceTextureDirectCHROMIUM(other_texture, mailbox.name);
context()->deleteTexture(other_texture);
context()->genSyncToken(list[0].mailbox_holder.sync_token.GetData());
EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
// Delete the resource, which shouldn't do anything.
child_resource_provider_->RemoveImportedResource(resource);
EXPECT_EQ(1u, context()->NumTextures());
EXPECT_FALSE(release_sync_token.HasData());
// Then receive the resource which should release the mailbox, expect the
// sync points to be consistent.
std::vector<viz::ReturnedResource> returned =
viz::TransferableResource::ReturnResources(list);
child_resource_provider_->ReceiveReturnsFromParent(returned);
EXPECT_LE(list[0].mailbox_holder.sync_token.release_count(),
release_sync_token.release_count());
EXPECT_FALSE(lost_resource);
}
context()->waitSyncToken(release_sync_token.GetConstData());
texture = context()->createAndConsumeTextureCHROMIUM(mailbox.name);
context()->deleteTexture(texture);
}
TEST_P(ResourceProviderTest, LostResourceInParent) {
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
viz::ResourceId id;
if (use_gpu()) {
id = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
} else {
id =
child_resource_provider_->CreateBitmapResource(size, gfx::ColorSpace());
}
child_resource_provider_->AllocateForTesting(id);
// Expect a GL resource to be lost.
bool should_lose_resource = use_gpu();
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
// Transfer the resource to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
EXPECT_EQ(1u, list.size());
resource_provider_->ReceiveFromChild(child_id, list);
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(id);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
resource_ids_to_receive);
}
// Lose the output surface in the parent.
resource_provider_->DidLoseContextProvider();
{
EXPECT_EQ(0u, returned_to_child.size());
// Transfer resources back from the parent to the child. Set no resources as
// being in use.
viz::ResourceIdSet no_resources;
resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
// Expect a GL resource to be lost.
ASSERT_EQ(1u, returned_to_child.size());
EXPECT_EQ(should_lose_resource, returned_to_child[0].lost);
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
returned_to_child.clear();
}
// A GL resource should be lost.
EXPECT_EQ(should_lose_resource, child_resource_provider_->IsLost(id));
// Lost resources stay in use in the parent forever.
EXPECT_EQ(should_lose_resource,
child_resource_provider_->InUseByConsumer(id));
}
TEST_P(ResourceProviderTest, LostMailboxInParent) {
gpu::SyncToken release_sync_token;
bool lost_resource = false;
bool release_called = false;
gpu::SyncToken sync_token;
viz::ResourceId resource = CreateChildMailbox(
&release_sync_token, &lost_resource, &release_called, &sync_token);
std::vector<viz::ReturnedResource> returned_to_child;
int child_id =
resource_provider_->CreateChild(GetReturnCallback(&returned_to_child));
{
// Transfer the resource to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(resource);
std::vector<viz::TransferableResource> list;
child_resource_provider_->PrepareSendToParent(resource_ids_to_transfer,
&list);
EXPECT_EQ(1u, list.size());
resource_provider_->ReceiveFromChild(child_id, list);
viz::ResourceIdSet resource_ids_to_receive;
resource_ids_to_receive.insert(resource);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
resource_ids_to_receive);
}
// Lose the output surface in the parent.
resource_provider_->DidLoseContextProvider();
{
EXPECT_EQ(0u, returned_to_child.size());
// Transfer resources back from the parent to the child. Set no resources as
// being in use.
viz::ResourceIdSet no_resources;
resource_provider_->DeclareUsedResourcesFromChild(child_id, no_resources);
ASSERT_EQ(1u, returned_to_child.size());
// Losing an output surface only loses hardware resources.
EXPECT_EQ(returned_to_child[0].lost, use_gpu());
child_resource_provider_->ReceiveReturnsFromParent(returned_to_child);
returned_to_child.clear();
}
// Delete the resource in the child. Expect the resource to be lost if it's
// a GL texture.
child_resource_provider_->RemoveImportedResource(resource);
EXPECT_EQ(lost_resource, use_gpu());
}
TEST_P(ResourceProviderTest, Shutdown) {
enum Cases {
// If not exported, the resource is returned not lost, and doesn't need a
// sync token.
kShutdownWithoutExport = 0,
// If exported and not returned, the resource is lost, and doesn't need/have
// a sync token to give.
kShutdownAfterExport,
// If exported and returned, the resource is not lost.
kShutdownAfterExportAndReturn,
// If returned as lost, then the resource must report lost on shutdown too.
kShutdownAfterExportAndReturnWithLostResource,
// If the context is lost, it doesn't affect imported resources as they are
// just weak references.
kShutdownAfterContextLossWithoutExport,
// If the context is lost, it doesn't affect imported resources as they are
// just weak references.
kShutdownAfterContextLossAfterExportAndReturn,
kNumCases,
};
for (int i = 0; i < kNumCases; ++i) {
auto c = static_cast<Cases>(i);
SCOPED_TRACE(c);
gpu::SyncToken release_sync_token;
bool lost_resource = false;
bool release_called = false;
gpu::SyncToken sync_token;
viz::ResourceId id = CreateChildMailbox(&release_sync_token, &lost_resource,
&release_called, &sync_token);
if (i == kShutdownAfterExport || i == kShutdownAfterExportAndReturn ||
i == kShutdownAfterExportAndReturnWithLostResource ||
i == kShutdownAfterContextLossAfterExportAndReturn) {
std::vector<viz::TransferableResource> send_list;
child_resource_provider_->PrepareSendToParent({id}, &send_list);
}
if (i == kShutdownAfterExportAndReturn ||
i == kShutdownAfterExportAndReturnWithLostResource ||
i == kShutdownAfterContextLossAfterExportAndReturn) {
viz::ReturnedResource r;
r.id = id;
r.sync_token = sync_token;
r.count = 1;
r.lost = (i == kShutdownAfterExportAndReturnWithLostResource);
child_resource_provider_->ReceiveReturnsFromParent({r});
}
EXPECT_FALSE(release_sync_token.HasData());
EXPECT_FALSE(lost_resource);
// Shutdown!
child_resource_provider_ = nullptr;
// If the resource was exported and returned, then it should come with a
// sync token.
if (use_gpu()) {
if (i == kShutdownAfterExportAndReturn ||
i == kShutdownAfterExportAndReturnWithLostResource ||
i == kShutdownAfterContextLossAfterExportAndReturn) {
EXPECT_LE(sync_token.release_count(),
release_sync_token.release_count());
}
}
// We always get the ReleaseCallback called.
EXPECT_TRUE(release_called);
bool expect_lost = i == kShutdownAfterExport ||
i == kShutdownAfterExportAndReturnWithLostResource;
EXPECT_EQ(expect_lost, lost_resource);
// Recreate it for the next case.
MakeChildResourceProvider();
}
}
TEST_P(ResourceProviderTest, ScopedSampler) {
// Sampling is only supported for GL textures.
if (!use_gpu())
return;
auto context_owned = std::make_unique<TextureStateTrackingContext>();
TextureStateTrackingContext* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
viz::ResourceSettings resource_settings = CreateResourceSettings();
auto resource_provider(std::make_unique<DisplayResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get()));
auto child_context_owned = std::make_unique<TextureStateTrackingContext>();
TextureStateTrackingContext* child_context = child_context_owned.get();
auto child_context_provider =
TestContextProvider::Create(std::move(child_context_owned));
child_context_provider->BindToCurrentThread();
auto child_resource_provider(std::make_unique<LayerTreeResourceProvider>(
child_context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
resource_settings));
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
int texture_id = 1;
viz::ResourceId id = child_resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
// Check that the texture gets created with the right sampler settings.
EXPECT_CALL(*child_context, bindTexture(GL_TEXTURE_2D, texture_id))
.Times(2); // Once to create and once to allocate.
EXPECT_CALL(*child_context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
EXPECT_CALL(*child_context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
EXPECT_CALL(*child_context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE));
EXPECT_CALL(*child_context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE));
child_resource_provider->AllocateForTesting(id);
Mock::VerifyAndClearExpectations(child_context);
// Return the mapped resource id.
ResourceProvider::ResourceIdMap resource_map =
SendResourceAndGetChildToParentMap({id}, resource_provider.get(),
child_resource_provider.get());
viz::ResourceId mapped_id = resource_map[id];
resource_provider->WaitSyncToken(mapped_id);
int parent_texture_id = 3;
// Creating a sampler with the default filter should not change any texture
// parameters.
{
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(parent_texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, parent_texture_id));
DisplayResourceProvider::ScopedSamplerGL sampler(
resource_provider.get(), mapped_id, GL_TEXTURE_2D, GL_LINEAR);
Mock::VerifyAndClearExpectations(context);
}
// Using a different filter should be reflected in the texture parameters.
{
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, parent_texture_id));
EXPECT_CALL(
*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
EXPECT_CALL(
*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
DisplayResourceProvider::ScopedSamplerGL sampler(
resource_provider.get(), mapped_id, GL_TEXTURE_2D, GL_NEAREST);
Mock::VerifyAndClearExpectations(context);
}
// Test resetting to the default filter.
{
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, parent_texture_id));
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
DisplayResourceProvider::ScopedSamplerGL sampler(
resource_provider.get(), mapped_id, GL_TEXTURE_2D, GL_LINEAR);
Mock::VerifyAndClearExpectations(context);
}
}
TEST_P(ResourceProviderTest, ManagedResource) {
// Sampling is only supported for GL textures.
if (!use_gpu())
return;
auto context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
int texture_id = 1;
// Check that the texture gets created with the right sampler settings.
viz::ResourceId id = resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
EXPECT_CALL(
*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
EXPECT_CALL(
*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
resource_provider->CreateForTesting(id);
EXPECT_NE(0u, id);
Mock::VerifyAndClearExpectations(context);
}
TEST_P(ResourceProviderTest, TextureWrapMode) {
// Sampling is only supported for GL textures.
if (!use_gpu())
return;
auto context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
for (int texture_id = 1; texture_id <= 2; ++texture_id) {
// Check that the texture gets created with the right sampler settings.
viz::ResourceId id = resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
GL_CLAMP_TO_EDGE));
EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
GL_CLAMP_TO_EDGE));
resource_provider->CreateForTesting(id);
EXPECT_NE(0u, id);
Mock::VerifyAndClearExpectations(context);
}
}
TEST_P(ResourceProviderTest, TextureHint) {
// Sampling is only supported for GL textures.
if (!use_gpu())
return;
auto context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* context = context_owned.get();
context->set_support_texture_storage(true);
context->set_support_texture_usage(true);
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
const viz::ResourceTextureHint hints[] = {
viz::ResourceTextureHint::kDefault,
viz::ResourceTextureHint::kFramebuffer,
};
for (GLuint texture_id = 1; texture_id <= arraysize(hints); ++texture_id) {
// Check that the texture gets created with the right sampler settings.
viz::ResourceId id = resource_provider->CreateGpuTextureResource(
size, hints[texture_id - 1], format, gfx::ColorSpace());
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
EXPECT_CALL(
*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
EXPECT_CALL(
*context,
texParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
// Check that GL_TEXTURE_USAGE_ANGLE is set iff the kFramebuffer hint is
// used.
bool is_framebuffer_hint =
hints[texture_id - 1] & viz::ResourceTextureHint::kFramebuffer;
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D,
GL_TEXTURE_USAGE_ANGLE,
GL_FRAMEBUFFER_ATTACHMENT_ANGLE))
.Times(is_framebuffer_hint ? 1 : 0);
resource_provider->CreateForTesting(id);
EXPECT_NE(0u, id);
Mock::VerifyAndClearExpectations(context);
}
}
TEST_P(ResourceProviderTest, ImportedResource_SharedMemory) {
if (use_gpu())
return;
gfx::Size size(64, 64);
const uint32_t kBadBeef = 0xbadbeef;
std::unique_ptr<viz::SharedBitmap> shared_bitmap(
CreateAndFillSharedBitmap(shared_bitmap_manager_.get(), size, kBadBeef));
auto resource_provider(std::make_unique<DisplayResourceProvider>(
nullptr, shared_bitmap_manager_.get()));
auto child_resource_provider(std::make_unique<LayerTreeResourceProvider>(
nullptr, shared_bitmap_manager_.get(), gpu_memory_buffer_manager_.get(),
kDelegatedSyncPointsRequired, CreateResourceSettings()));
gpu::SyncToken release_sync_token;
bool lost_resource = false;
std::unique_ptr<viz::SingleReleaseCallback> callback =
viz::SingleReleaseCallback::Create(
base::Bind(&ReleaseCallback, &release_sync_token, &lost_resource));
auto resource = viz::TransferableResource::MakeSoftware(
shared_bitmap->id(), shared_bitmap->sequence_number(), size);
viz::ResourceId resource_id =
child_resource_provider->ImportResource(resource, std::move(callback));
EXPECT_NE(0u, resource_id);
// Transfer resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(resource_id);
std::vector<viz::TransferableResource> send_to_parent;
std::vector<viz::ReturnedResource> returned_to_child;
int child_id = resource_provider->CreateChild(
base::Bind(&CollectResources, &returned_to_child));
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&send_to_parent);
resource_provider->ReceiveFromChild(child_id, send_to_parent);
// In DisplayResourceProvider's namespace, use the mapped resource id.
ResourceProvider::ResourceIdMap resource_map =
resource_provider->GetChildToParentMap(child_id);
viz::ResourceId mapped_resource_id = resource_map[resource_id];
{
DisplayResourceProvider::ScopedReadLockSoftware lock(
resource_provider.get(), mapped_resource_id);
const SkBitmap* sk_bitmap = lock.sk_bitmap();
EXPECT_EQ(sk_bitmap->width(), size.width());
EXPECT_EQ(sk_bitmap->height(), size.height());
EXPECT_EQ(*sk_bitmap->getAddr32(16, 16), kBadBeef);
}
EXPECT_EQ(0u, returned_to_child.size());
// Transfer resources back from the parent to the child. Set no resources as
// being in use.
resource_provider->DeclareUsedResourcesFromChild(child_id,
viz::ResourceIdSet());
EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
child_resource_provider->RemoveImportedResource(resource_id);
EXPECT_FALSE(release_sync_token.HasData());
EXPECT_FALSE(lost_resource);
}
class ResourceProviderTestImportedResourceGLFilters
: public ResourceProviderTest {
public:
static void RunTest(
TestSharedBitmapManager* shared_bitmap_manager,
viz::TestGpuMemoryBufferManager* gpu_memory_buffer_manager,
bool mailbox_nearest_neighbor,
GLenum sampler_filter) {
auto context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* context = context_owned.get();
auto context_provider =
TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto resource_provider(std::make_unique<DisplayResourceProvider>(
context_provider.get(), shared_bitmap_manager));
auto child_context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* child_context = child_context_owned.get();
auto child_context_provider =
TestContextProvider::Create(std::move(child_context_owned));
child_context_provider->BindToCurrentThread();
auto child_resource_provider(std::make_unique<LayerTreeResourceProvider>(
child_context_provider.get(), shared_bitmap_manager,
gpu_memory_buffer_manager, kDelegatedSyncPointsRequired,
CreateResourceSettings()));
unsigned texture_id = 1;
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x12),
0x34);
const GLuint64 current_fence_sync = child_context->GetNextFenceSync();
EXPECT_CALL(*child_context, bindTexture(_, _)).Times(0);
EXPECT_CALL(*child_context, waitSyncToken(_)).Times(0);
EXPECT_CALL(*child_context, produceTextureDirectCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*child_context, createAndConsumeTextureCHROMIUM(_)).Times(0);
gpu::Mailbox gpu_mailbox;
memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
gpu::SyncToken release_sync_token;
bool lost_resource = false;
std::unique_ptr<viz::SingleReleaseCallback> callback =
viz::SingleReleaseCallback::Create(
base::Bind(&ReleaseCallback, &release_sync_token, &lost_resource));
GLuint filter = mailbox_nearest_neighbor ? GL_NEAREST : GL_LINEAR;
auto resource = viz::TransferableResource::MakeGL(
gpu_mailbox, filter, GL_TEXTURE_2D, sync_token);
viz::ResourceId resource_id =
child_resource_provider->ImportResource(resource, std::move(callback));
EXPECT_NE(0u, resource_id);
EXPECT_EQ(current_fence_sync, child_context->GetNextFenceSync());
Mock::VerifyAndClearExpectations(child_context);
// Transfer resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(resource_id);
std::vector<viz::TransferableResource> send_to_parent;
std::vector<viz::ReturnedResource> returned_to_child;
int child_id = resource_provider->CreateChild(
base::Bind(&CollectResources, &returned_to_child));
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&send_to_parent);
resource_provider->ReceiveFromChild(child_id, send_to_parent);
// In DisplayResourceProvider's namespace, use the mapped resource id.
ResourceProvider::ResourceIdMap resource_map =
resource_provider->GetChildToParentMap(child_id);
viz::ResourceId mapped_resource_id = resource_map[resource_id];
{
// The verified flush flag will be set by
// LayerTreeResourceProvider::PrepareSendToParent. Before checking if
// the gpu::SyncToken matches, set this flag first.
sync_token.SetVerifyFlush();
// Mailbox sync point WaitSyncToken before using the texture.
EXPECT_CALL(*context, waitSyncToken(MatchesSyncToken(sync_token)));
resource_provider->WaitSyncToken(mapped_resource_id);
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _)).Times(0);
// The sampler will reset these if |mailbox_nearest_neighbor| does not
// match |sampler_filter|.
if (mailbox_nearest_neighbor != (sampler_filter == GL_NEAREST)) {
EXPECT_CALL(*context, texParameteri(
GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, sampler_filter));
EXPECT_CALL(*context, texParameteri(
GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, sampler_filter));
}
DisplayResourceProvider::ScopedSamplerGL lock(
resource_provider.get(), mapped_resource_id, sampler_filter);
Mock::VerifyAndClearExpectations(context);
EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());
// When done with it, a sync point should be inserted, but no produce is
// necessary.
EXPECT_CALL(*child_context, bindTexture(_, _)).Times(0);
EXPECT_CALL(*child_context, produceTextureDirectCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*child_context, waitSyncToken(_)).Times(0);
EXPECT_CALL(*child_context, createAndConsumeTextureCHROMIUM(_)).Times(0);
}
EXPECT_EQ(0u, returned_to_child.size());
// Transfer resources back from the parent to the child. Set no resources as
// being in use.
resource_provider->DeclareUsedResourcesFromChild(child_id,
viz::ResourceIdSet());
EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
child_resource_provider->RemoveImportedResource(resource_id);
EXPECT_TRUE(release_sync_token.HasData());
EXPECT_FALSE(lost_resource);
}
};
TEST_P(ResourceProviderTest, ImportedResource_GLTexture2D_LinearToLinear) {
// Mailboxing is only supported for GL textures.
if (!use_gpu())
return;
ResourceProviderTestImportedResourceGLFilters::RunTest(
shared_bitmap_manager_.get(), gpu_memory_buffer_manager_.get(), false,
GL_LINEAR);
}
TEST_P(ResourceProviderTest, ImportedResource_GLTexture2D_NearestToNearest) {
// Mailboxing is only supported for GL textures.
if (!use_gpu())
return;
ResourceProviderTestImportedResourceGLFilters::RunTest(
shared_bitmap_manager_.get(), gpu_memory_buffer_manager_.get(), true,
GL_NEAREST);
}
TEST_P(ResourceProviderTest, ImportedResource_GLTexture2D_NearestToLinear) {
// Mailboxing is only supported for GL textures.
if (!use_gpu())
return;
ResourceProviderTestImportedResourceGLFilters::RunTest(
shared_bitmap_manager_.get(), gpu_memory_buffer_manager_.get(), true,
GL_LINEAR);
}
TEST_P(ResourceProviderTest, ImportedResource_GLTexture2D_LinearToNearest) {
// Mailboxing is only supported for GL textures.
if (!use_gpu())
return;
ResourceProviderTestImportedResourceGLFilters::RunTest(
shared_bitmap_manager_.get(), gpu_memory_buffer_manager_.get(), false,
GL_NEAREST);
}
TEST_P(ResourceProviderTest, ImportedResource_GLTextureExternalOES) {
// Mailboxing is only supported for GL textures.
if (!use_gpu())
return;
auto context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto resource_provider(std::make_unique<DisplayResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get()));
auto child_context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* child_context = child_context_owned.get();
auto child_context_provider =
TestContextProvider::Create(std::move(child_context_owned));
child_context_provider->BindToCurrentThread();
auto child_resource_provider(std::make_unique<LayerTreeResourceProvider>(
child_context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
const GLuint64 current_fence_sync = child_context->GetNextFenceSync();
EXPECT_CALL(*child_context, bindTexture(_, _)).Times(0);
EXPECT_CALL(*child_context, waitSyncToken(_)).Times(0);
EXPECT_CALL(*child_context, produceTextureDirectCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*child_context, createAndConsumeTextureCHROMIUM(_)).Times(0);
gpu::Mailbox gpu_mailbox;
memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
std::unique_ptr<viz::SingleReleaseCallback> callback =
viz::SingleReleaseCallback::Create(base::Bind(&EmptyReleaseCallback));
auto resource = viz::TransferableResource::MakeGL(
gpu_mailbox, GL_LINEAR, GL_TEXTURE_EXTERNAL_OES, sync_token);
viz::ResourceId resource_id =
child_resource_provider->ImportResource(resource, std::move(callback));
EXPECT_NE(0u, resource_id);
EXPECT_EQ(current_fence_sync, child_context->GetNextFenceSync());
Mock::VerifyAndClearExpectations(child_context);
// Transfer resources to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(resource_id);
std::vector<viz::TransferableResource> send_to_parent;
std::vector<viz::ReturnedResource> returned_to_child;
int child_id = resource_provider->CreateChild(
base::Bind(&CollectResources, &returned_to_child));
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&send_to_parent);
resource_provider->ReceiveFromChild(child_id, send_to_parent);
// Before create DrawQuad in DisplayResourceProvider's namespace, get the
// mapped resource id first.
ResourceProvider::ResourceIdMap resource_map =
resource_provider->GetChildToParentMap(child_id);
viz::ResourceId mapped_resource_id = resource_map[resource_id];
{
// The verified flush flag will be set by
// LayerTreeResourceProvider::PrepareSendToParent. Before checking if
// the gpu::SyncToken matches, set this flag first.
sync_token.SetVerifyFlush();
// Mailbox sync point WaitSyncToken before using the texture.
EXPECT_CALL(*context, waitSyncToken(MatchesSyncToken(sync_token)));
resource_provider->WaitSyncToken(mapped_resource_id);
Mock::VerifyAndClearExpectations(context);
unsigned texture_id = 1;
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(texture_id));
EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _)).Times(0);
DisplayResourceProvider::ScopedReadLockGL lock(resource_provider.get(),
mapped_resource_id);
Mock::VerifyAndClearExpectations(context);
// When done with it, a sync point should be inserted, but no produce is
// necessary.
EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_)).Times(0);
Mock::VerifyAndClearExpectations(context);
}
EXPECT_EQ(0u, returned_to_child.size());
// Transfer resources back from the parent to the child. Set no resources as
// being in use.
resource_provider->DeclareUsedResourcesFromChild(child_id,
viz::ResourceIdSet());
EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider->ReceiveReturnsFromParent(returned_to_child);
child_resource_provider->RemoveImportedResource(resource_id);
}
TEST_P(ResourceProviderTest, WaitSyncTokenIfNeeded_ResourceFromChild) {
// Mailboxing is only supported for GL textures.
if (!use_gpu())
return;
auto context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto resource_provider = std::make_unique<DisplayResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get());
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
const GLuint64 current_fence_sync = context->GetNextFenceSync();
EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_)).Times(0);
// Receive a resource from the child.
std::vector<viz::ReturnedResource> returned;
ReturnCallback return_callback = base::Bind(
[](std::vector<viz::ReturnedResource>* out,
const std::vector<viz::ReturnedResource>& in) {
*out = std::move(in);
},
&returned);
int child = resource_provider->CreateChild(return_callback);
// Send a bunch of resources, to make sure things work when there's more than
// one resource, and to make vectors reallocate and such.
for (int i = 0; i < 100; ++i) {
gpu::Mailbox gpu_mailbox;
memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
auto resource = viz::TransferableResource::MakeGL(
gpu_mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token);
resource.id = i;
resource_provider->ReceiveFromChild(child, {resource});
auto& map = resource_provider->GetChildToParentMap(child);
EXPECT_EQ(i + 1u, map.size());
}
EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());
resource_provider.reset();
// Returned resources don't have InsertFenceSyncCHROMIUM() called.
EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());
// The returned resource has a verified sync token.
ASSERT_EQ(returned.size(), 100u);
for (viz::ReturnedResource& r : returned)
EXPECT_TRUE(r.sync_token.verified_flush());
}
TEST_P(ResourceProviderTest, WaitSyncTokenIfNeeded_WithSyncToken) {
// Mailboxing is only supported for GL textures.
if (!use_gpu())
return;
auto context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto resource_provider = std::make_unique<DisplayResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get());
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
const GLuint64 current_fence_sync = context->GetNextFenceSync();
EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_)).Times(0);
viz::ResourceId id = MakeGpuResourceAndSendToDisplay(
'a', GL_LINEAR, GL_TEXTURE_2D, sync_token, resource_provider.get());
EXPECT_NE(0u, id);
EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());
Mock::VerifyAndClearExpectations(context);
{
// First call to WaitSyncToken should call waitSyncToken.
EXPECT_CALL(*context, waitSyncToken(MatchesSyncToken(sync_token)));
resource_provider->WaitSyncToken(id);
Mock::VerifyAndClearExpectations(context);
// Subsequent calls to WaitSyncToken shouldn't call waitSyncToken.
EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
resource_provider->WaitSyncToken(id);
Mock::VerifyAndClearExpectations(context);
}
}
TEST_P(ResourceProviderTest,
ImportedResource_WaitSyncTokenIfNeeded_NoSyncToken) {
// Mailboxing is only supported for GL textures.
if (!use_gpu())
return;
auto context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto resource_provider = std::make_unique<DisplayResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get());
gpu::SyncToken sync_token;
const GLuint64 current_fence_sync = context->GetNextFenceSync();
EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_)).Times(0);
viz::ResourceId id = MakeGpuResourceAndSendToDisplay(
'h', GL_LINEAR, GL_TEXTURE_2D, sync_token, resource_provider.get());
EXPECT_EQ(current_fence_sync, context->GetNextFenceSync());
EXPECT_NE(0u, id);
Mock::VerifyAndClearExpectations(context);
{
// WaitSyncToken with empty sync_token shouldn't call waitSyncToken.
EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
resource_provider->WaitSyncToken(id);
Mock::VerifyAndClearExpectations(context);
}
}
TEST_P(ResourceProviderTest, ImportedResource_PrepareSendToParent_NoSyncToken) {
// Mailboxing is only supported for GL textures.
if (!use_gpu())
return;
auto context_owned(std::make_unique<TextureStateTrackingContext>());
TextureStateTrackingContext* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
EXPECT_CALL(*context, waitSyncToken(_)).Times(0);
EXPECT_CALL(*context, produceTextureDirectCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_)).Times(0);
auto resource = viz::TransferableResource::MakeGL(
gpu::Mailbox::Generate(), GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken());
std::unique_ptr<viz::SingleReleaseCallback> callback =
viz::SingleReleaseCallback::Create(base::Bind(&EmptyReleaseCallback));
viz::ResourceId id =
resource_provider->ImportResource(resource, std::move(callback));
EXPECT_NE(0u, id);
Mock::VerifyAndClearExpectations(context);
ResourceProvider::ResourceIdArray resource_ids_to_transfer{id};
std::vector<viz::TransferableResource> list;
resource_provider->PrepareSendToParent(resource_ids_to_transfer, &list);
ASSERT_EQ(1u, list.size());
EXPECT_FALSE(list[0].mailbox_holder.sync_token.HasData());
EXPECT_TRUE(list[0].mailbox_holder.sync_token.verified_flush());
Mock::VerifyAndClearExpectations(context);
}
class AllocationTrackingContext3D : public TextureStateTrackingContext {
public:
MOCK_METHOD0(NextTextureId, GLuint());
MOCK_METHOD1(RetireTextureId, void(GLuint id));
MOCK_METHOD5(texStorage2DImageCHROMIUM,
void(GLenum target,
GLenum internalformat,
GLenum bufferusage,
GLsizei width,
GLsizei height));
MOCK_METHOD5(texStorage2DEXT,
void(GLenum target,
GLsizei levels,
GLenum internalformat,
GLsizei width,
GLsizei height));
MOCK_METHOD9(texImage2D,
void(GLenum target,
GLint level,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const void* pixels));
MOCK_METHOD9(texSubImage2D,
void(GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
const void* pixels));
MOCK_METHOD9(asyncTexImage2DCHROMIUM,
void(GLenum target,
GLint level,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const void* pixels));
MOCK_METHOD9(asyncTexSubImage2DCHROMIUM,
void(GLenum target,
GLint level,
GLint xoffset,
GLint yoffset,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
const void* pixels));
MOCK_METHOD8(compressedTexImage2D,
void(GLenum target,
GLint level,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLint border,
GLsizei image_size,
const void* data));
MOCK_METHOD1(waitAsyncTexImage2DCHROMIUM, void(GLenum));
MOCK_METHOD4(createImageCHROMIUM,
GLuint(ClientBuffer, GLsizei, GLsizei, GLenum));
MOCK_METHOD1(destroyImageCHROMIUM, void(GLuint));
MOCK_METHOD2(bindTexImage2DCHROMIUM, void(GLenum, GLint));
MOCK_METHOD2(releaseTexImage2DCHROMIUM, void(GLenum, GLint));
MOCK_METHOD3(texParameteri, void(GLenum target, GLenum pname, GLint param));
};
TEST_P(ResourceProviderTest, TextureAllocation) {
if (!use_gpu())
return;
std::unique_ptr<AllocationTrackingContext3D> context_owned(
new StrictMock<AllocationTrackingContext3D>);
AllocationTrackingContext3D* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
gfx::Size size(2, 2);
viz::ResourceFormat format = viz::RGBA_8888;
viz::ResourceId id = 0;
uint8_t pixels[16] = { 0 };
int texture_id = 123;
EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber());
// Lazy allocation. Don't allocate when creating the resource.
id = resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(1);
resource_provider->CreateForTesting(id);
EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
resource_provider->DeleteResource(id);
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber());
// Do allocate when we set the pixels.
id = resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3);
EXPECT_CALL(*context, texImage2D(GL_TEXTURE_2D, _, _, 2, 2, _, _, _, _))
.Times(1);
EXPECT_CALL(*context, texSubImage2D(GL_TEXTURE_2D, _, _, _, 2, 2, _, _, _))
.Times(1);
resource_provider->CopyToResource(id, pixels, size);
EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
resource_provider->DeleteResource(id);
Mock::VerifyAndClearExpectations(context);
}
TEST_P(ResourceProviderTest, TextureStorageAllocation) {
if (!use_gpu())
return;
auto context_owned =
std::make_unique<StrictMock<AllocationTrackingContext3D>>();
auto* context = context_owned.get();
context->set_support_texture_storage(true);
context->set_support_texture_usage(true);
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto child_resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
viz::ResourceId id = child_resource_provider->CreateGpuTextureResource(
gfx::Size(2, 2), viz::ResourceTextureHint::kDefault, viz::RGBA_8888,
gfx::ColorSpace());
const GLuint kTextureId = 123u;
EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(kTextureId));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId)).Times(2);
EXPECT_CALL(*context, texStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, 2, 2))
.Times(1);
EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, _, _)).Times(AnyNumber());
child_resource_provider->AllocateForTesting(id);
EXPECT_CALL(*context, RetireTextureId(kTextureId));
child_resource_provider->DeleteResource(id);
}
TEST_P(ResourceProviderTest, ScopedWriteLockGpuMemoryBuffer) {
if (!use_gpu())
return;
std::unique_ptr<AllocationTrackingContext3D> context_owned(
new StrictMock<AllocationTrackingContext3D>);
AllocationTrackingContext3D* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
const int kWidth = 2;
const int kHeight = 2;
viz::ResourceFormat format = viz::RGBA_8888;
const unsigned kTextureId = 123u;
const unsigned kImageId = 234u;
auto resource_provider = std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings());
viz::ResourceId id = resource_provider->CreateGpuMemoryBufferResource(
gfx::Size(kWidth, kHeight), viz::ResourceTextureHint::kDefault, format,
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE, gfx::ColorSpace());
InSequence sequence;
// Create texture and image upon releasing the lock.
EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(kTextureId));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId));
EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber());
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId));
EXPECT_CALL(*context, createImageCHROMIUM(_, kWidth, kHeight, GL_RGBA))
.WillOnce(Return(kImageId));
EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId));
{
LayerTreeResourceProvider::ScopedWriteLockGpuMemoryBuffer lock(
resource_provider.get(), id);
EXPECT_TRUE(lock.GetGpuMemoryBuffer());
}
Mock::VerifyAndClearExpectations(context);
// Upload to GPU again since image is dirty after the write lock.
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId));
EXPECT_CALL(*context, releaseTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId));
EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId));
{
LayerTreeResourceProvider::ScopedWriteLockGpuMemoryBuffer lock(
resource_provider.get(), id);
EXPECT_TRUE(lock.GetGpuMemoryBuffer());
}
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, destroyImageCHROMIUM(kImageId));
EXPECT_CALL(*context, RetireTextureId(kTextureId));
}
TEST_P(ResourceProviderTest, CompressedTextureETC1Allocate) {
if (!use_gpu())
return;
std::unique_ptr<AllocationTrackingContext3D> context_owned(
new AllocationTrackingContext3D);
AllocationTrackingContext3D* context = context_owned.get();
context_owned->set_support_compressed_texture_etc1(true);
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
gfx::Size size(4, 4);
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
int texture_id = 123;
viz::ResourceId id = resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, viz::ETC1, gfx::ColorSpace());
EXPECT_NE(0u, id);
EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(1);
resource_provider->AllocateForTesting(id);
EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
resource_provider->DeleteResource(id);
}
TEST_P(ResourceProviderTest, CompressedTextureETC1Upload) {
if (!use_gpu())
return;
std::unique_ptr<AllocationTrackingContext3D> context_owned(
new AllocationTrackingContext3D);
AllocationTrackingContext3D* context = context_owned.get();
context_owned->set_support_compressed_texture_etc1(true);
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
gfx::Size size(4, 4);
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
int texture_id = 123;
uint8_t pixels[8];
viz::ResourceId id = resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, viz::ETC1, gfx::ColorSpace());
EXPECT_NE(0u, id);
EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
EXPECT_CALL(*context,
compressedTexImage2D(
_, 0, _, size.width(), size.height(), _, _, _)).Times(1);
resource_provider->CopyToResource(id, pixels, size);
EXPECT_CALL(*context, RetireTextureId(texture_id)).Times(1);
resource_provider->DeleteResource(id);
}
INSTANTIATE_TEST_CASE_P(ResourceProviderTests,
ResourceProviderTest,
::testing::Values(true, false));
class TextureIdAllocationTrackingContext : public TestWebGraphicsContext3D {
public:
GLuint NextTextureId() override {
base::AutoLock lock(namespace_->lock);
return namespace_->next_texture_id++;
}
void RetireTextureId(GLuint) override {}
GLuint PeekTextureId() {
base::AutoLock lock(namespace_->lock);
return namespace_->next_texture_id;
}
};
TEST(ResourceProviderTest, TextureAllocationChunkSize) {
auto context_owned(std::make_unique<TextureIdAllocationTrackingContext>());
TextureIdAllocationTrackingContext* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
auto shared_bitmap_manager = std::make_unique<TestSharedBitmapManager>();
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
{
size_t kTextureAllocationChunkSize = 1;
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager.get(), nullptr,
kDelegatedSyncPointsRequired,
CreateResourceSettings(kTextureAllocationChunkSize)));
viz::ResourceId id = resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
resource_provider->AllocateForTesting(id);
Mock::VerifyAndClearExpectations(context);
DCHECK_EQ(2u, context->PeekTextureId());
resource_provider->DeleteResource(id);
}
{
size_t kTextureAllocationChunkSize = 8;
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager.get(), nullptr,
kDelegatedSyncPointsRequired,
CreateResourceSettings(kTextureAllocationChunkSize)));
viz::ResourceId id = resource_provider->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
resource_provider->AllocateForTesting(id);
Mock::VerifyAndClearExpectations(context);
DCHECK_EQ(10u, context->PeekTextureId());
resource_provider->DeleteResource(id);
}
}
TEST_P(ResourceProviderTest, GetSyncTokenForResources) {
if (!use_gpu())
return;
gfx::Size size(1, 1);
viz::ResourceFormat format = viz::RGBA_8888;
// ~Random set of |release_count|s to set on sync tokens.
uint64_t release_counts[5] = {7, 3, 10, 2, 5};
ResourceProvider::ResourceIdArray array;
for (uint32_t i = 0; i < arraysize(release_counts); ++i) {
viz::ResourceId id = child_resource_provider_->CreateGpuTextureResource(
size, viz::ResourceTextureHint::kDefault, format, gfx::ColorSpace());
array.push_back(id);
LayerTreeResourceProvider::ScopedWriteLockGL lock(
child_resource_provider_.get(), id);
gpu::SyncToken token;
token.Set(gpu::CommandBufferNamespace::INVALID, gpu::CommandBufferId(),
release_counts[i]);
lock.set_sync_token(token);
}
gpu::SyncToken last_token =
child_resource_provider_->GetSyncTokenForResources(array);
EXPECT_EQ(last_token.release_count(), 10u);
}
TEST_P(ResourceProviderTest, ScopedWriteLockGL) {
if (!use_gpu())
return;
std::unique_ptr<AllocationTrackingContext3D> context_owned(
new StrictMock<AllocationTrackingContext3D>);
AllocationTrackingContext3D* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
const int kWidth = 2;
const int kHeight = 2;
const viz::ResourceFormat format = viz::RGBA_8888;
const unsigned kTextureId = 123u;
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
viz::ResourceId id = resource_provider->CreateGpuTextureResource(
gfx::Size(kWidth, kHeight), viz::ResourceTextureHint::kDefault, format,
gfx::ColorSpace());
InSequence sequence;
// First use will allocate lazily when accessing the texture.
{
EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(kTextureId));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId));
EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber());
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId));
EXPECT_CALL(*context, texImage2D(GL_TEXTURE_2D, 0, GLInternalFormat(format),
kWidth, kHeight, 0, GLDataFormat(format),
GLDataType(format), nullptr));
LayerTreeResourceProvider::ScopedWriteLockGL lock(resource_provider.get(),
id);
EXPECT_EQ(lock.GetTexture(), kTextureId);
Mock::VerifyAndClearExpectations(context);
}
// Subsequent uses will not allocate.
{
LayerTreeResourceProvider::ScopedWriteLockGL lock(resource_provider.get(),
id);
EXPECT_EQ(lock.GetTexture(), kTextureId);
}
EXPECT_CALL(*context, RetireTextureId(kTextureId));
resource_provider->DeleteResource(id);
}
TEST_P(ResourceProviderTest, ScopedWriteLockGL_Overlay) {
if (!use_gpu())
return;
std::unique_ptr<AllocationTrackingContext3D> context_owned(
new StrictMock<AllocationTrackingContext3D>);
AllocationTrackingContext3D* context = context_owned.get();
context->set_support_texture_storage_image(true);
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
const int kWidth = 2;
const int kHeight = 2;
const viz::ResourceFormat format = viz::RGBA_8888;
const unsigned kTextureId = 123u;
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
viz::ResourceId id = resource_provider->CreateGpuTextureResource(
gfx::Size(kWidth, kHeight), viz::ResourceTextureHint::kOverlay, format,
gfx::ColorSpace());
InSequence sequence;
// First use will allocate lazily on accessing the texture.
{
EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(kTextureId));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId));
EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber());
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId));
EXPECT_CALL(*context, texStorage2DImageCHROMIUM(GL_TEXTURE_2D, GL_RGBA8_OES,
GL_SCANOUT_CHROMIUM, kWidth,
kHeight));
LayerTreeResourceProvider::ScopedWriteLockGL lock(resource_provider.get(),
id);
EXPECT_EQ(lock.GetTexture(), kTextureId);
Mock::VerifyAndClearExpectations(context);
}
// Subsequent uses will not allocate.
{
LayerTreeResourceProvider::ScopedWriteLockGL lock(resource_provider.get(),
id);
EXPECT_EQ(lock.GetTexture(), kTextureId);
}
EXPECT_CALL(*context, RetireTextureId(kTextureId));
resource_provider->DeleteResource(id);
}
TEST_P(ResourceProviderTest, ScopedWriteLockRaster_Mailbox) {
if (!use_gpu())
return;
std::unique_ptr<AllocationTrackingContext3D> context_owned(
new StrictMock<AllocationTrackingContext3D>);
AllocationTrackingContext3D* context = context_owned.get();
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
const int kWidth = 2;
const int kHeight = 2;
const viz::ResourceFormat format = viz::RGBA_8888;
const unsigned kTextureId = 123u;
const unsigned kWorkerTextureId = 234u;
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
viz::ResourceId id = resource_provider->CreateGpuTextureResource(
gfx::Size(kWidth, kHeight), viz::ResourceTextureHint::kDefault, format,
gfx::ColorSpace());
InSequence sequence;
gpu::SyncToken sync_token;
// First use will create mailbox when lock is created and allocate lazily in
// ConsumeTexture.
{
EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(kTextureId));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId));
EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber());
LayerTreeResourceProvider::ScopedWriteLockRaster lock(
resource_provider.get(), id);
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, produceTextureDirectCHROMIUM(kTextureId, _));
lock.CreateMailbox();
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(kWorkerTextureId));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kWorkerTextureId));
EXPECT_CALL(*context, texImage2D(GL_TEXTURE_2D, 0, GLInternalFormat(format),
kWidth, kHeight, 0, GLDataFormat(format),
GLDataType(format), nullptr));
EXPECT_EQ(kWorkerTextureId,
lock.ConsumeTexture(context_provider->RasterInterface()));
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, RetireTextureId(kWorkerTextureId));
context_provider->ContextGL()->DeleteTextures(1, &kWorkerTextureId);
Mock::VerifyAndClearExpectations(context);
}
// Subsequent uses will not create mailbox or allocate.
{
LayerTreeResourceProvider::ScopedWriteLockRaster lock(
resource_provider.get(), id);
lock.CreateMailbox();
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(kWorkerTextureId));
EXPECT_EQ(kWorkerTextureId,
lock.ConsumeTexture(context_provider->RasterInterface()));
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, RetireTextureId(kWorkerTextureId));
context_provider->ContextGL()->DeleteTextures(1, &kWorkerTextureId);
sync_token = LayerTreeResourceProvider::GenerateSyncTokenHelper(
context_provider->RasterInterface());
lock.set_sync_token(sync_token);
Mock::VerifyAndClearExpectations(context);
}
// Wait for worker context sync token before deleting texture.
EXPECT_CALL(*context, waitSyncToken(MatchesSyncToken(sync_token)));
EXPECT_CALL(*context, RetireTextureId(kTextureId));
resource_provider->DeleteResource(id);
}
TEST_P(ResourceProviderTest, ScopedWriteLockRaster_Mailbox_Overlay) {
if (!use_gpu())
return;
std::unique_ptr<AllocationTrackingContext3D> context_owned(
new StrictMock<AllocationTrackingContext3D>);
AllocationTrackingContext3D* context = context_owned.get();
context->set_support_texture_storage_image(true);
auto context_provider = TestContextProvider::Create(std::move(context_owned));
context_provider->BindToCurrentThread();
const int kWidth = 2;
const int kHeight = 2;
const viz::ResourceFormat format = viz::RGBA_8888;
const unsigned kTextureId = 123u;
const unsigned kWorkerTextureId = 234u;
auto resource_provider(std::make_unique<LayerTreeResourceProvider>(
context_provider.get(), shared_bitmap_manager_.get(),
gpu_memory_buffer_manager_.get(), kDelegatedSyncPointsRequired,
CreateResourceSettings()));
viz::ResourceId id = resource_provider->CreateGpuTextureResource(
gfx::Size(kWidth, kHeight), viz::ResourceTextureHint::kOverlay, format,
gfx::ColorSpace());
InSequence sequence;
gpu::SyncToken sync_token;
// First use will create mailbox when lock is created and allocate lazily in
// ConsumeTexture.
{
EXPECT_CALL(*context, NextTextureId()).WillOnce(Return(kTextureId));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId));
EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber());
LayerTreeResourceProvider::ScopedWriteLockRaster lock(
resource_provider.get(), id);
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, produceTextureDirectCHROMIUM(kTextureId, _));
lock.CreateMailbox();
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(kWorkerTextureId));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kWorkerTextureId));
EXPECT_CALL(*context, texStorage2DImageCHROMIUM(GL_TEXTURE_2D, GL_RGBA8_OES,
GL_SCANOUT_CHROMIUM, kWidth,
kHeight));
EXPECT_EQ(kWorkerTextureId,
lock.ConsumeTexture(context_provider->RasterInterface()));
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, RetireTextureId(kWorkerTextureId));
context_provider->ContextGL()->DeleteTextures(1, &kWorkerTextureId);
Mock::VerifyAndClearExpectations(context);
}
// Subsequent uses will not create mailbox or allocate.
{
LayerTreeResourceProvider::ScopedWriteLockRaster lock(
resource_provider.get(), id);
lock.CreateMailbox();
Mock::VerifyAndClearExpectations(context);
EXPECT_CALL(*context, createAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(kWorkerTextureId));
EXPECT_EQ(kWorkerTextureId,
lock.ConsumeTexture(context_provider->RasterInterface()));
Mock::VerifyAndClearExpectations(context);
sync_token = LayerTreeResourceProvider::GenerateSyncTokenHelper(
context_provider->RasterInterface());
lock.set_sync_token(sync_token);
EXPECT_CALL(*context, RetireTextureId(kWorkerTextureId));
context_provider->ContextGL()->DeleteTextures(1, &kWorkerTextureId);
Mock::VerifyAndClearExpectations(context);
}
// Wait for worker context sync token before deleting texture.
EXPECT_CALL(*context, waitSyncToken(MatchesSyncToken(sync_token)));
EXPECT_CALL(*context, RetireTextureId(kTextureId));
resource_provider->DeleteResource(id);
}
} // namespace
} // namespace cc