blob: a23af0b577c7f5ce3ed255ff2881055eac99f8ff [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/resource_provider.h"
#include <algorithm>
#include "base/bind.h"
#include "base/containers/hash_tables.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "cc/base/scoped_ptr_deque.h"
#include "cc/output/output_surface.h"
#include "cc/test/fake_output_surface.h"
#include "cc/test/fake_output_surface_client.h"
#include "cc/test/test_web_graphics_context_3d.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#include "ui/gfx/rect.h"
using testing::Mock;
using testing::NiceMock;
using testing::Return;
using testing::SetArgPointee;
using testing::StrictMock;
using testing::_;
using WebKit::WGC3Dbyte;
using WebKit::WGC3Denum;
using WebKit::WGC3Dint;
using WebKit::WGC3Dsizei;
using WebKit::WGC3Duint;
using WebKit::WebGLId;
namespace cc {
namespace {
size_t TextureSize(gfx::Size size, WGC3Denum format) {
unsigned int components_per_pixel = 4;
unsigned int bytes_per_component = 1;
return size.width() * size.height() * components_per_pixel *
bytes_per_component;
}
struct Texture : public base::RefCounted<Texture> {
Texture() : format(0), filter(GL_NEAREST_MIPMAP_LINEAR) {}
void Reallocate(gfx::Size size, WGC3Denum format) {
this->size = size;
this->format = format;
this->data.reset(new uint8_t[TextureSize(size, format)]);
}
gfx::Size size;
WGC3Denum format;
WGC3Denum filter;
scoped_ptr<uint8_t[]> data;
private:
friend class base::RefCounted<Texture>;
~Texture() {}
};
// Shared data between multiple ResourceProviderContext. This contains mailbox
// contents as well as information about sync points.
class ContextSharedData {
public:
static scoped_ptr<ContextSharedData> Create() {
return make_scoped_ptr(new ContextSharedData());
}
unsigned InsertSyncPoint() { return next_sync_point_++; }
void GenMailbox(WGC3Dbyte* mailbox) {
memset(mailbox, 0, sizeof(WGC3Dbyte[64]));
memcpy(mailbox, &next_mailbox_, sizeof(next_mailbox_));
++next_mailbox_;
}
void ProduceTexture(const WGC3Dbyte* mailbox_name,
unsigned sync_point,
scoped_refptr<Texture> texture) {
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<Texture> ConsumeTexture(const WGC3Dbyte* mailbox_name,
unsigned sync_point) {
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_point) {
NOTREACHED();
return scoped_refptr<Texture>();
}
return textures_[mailbox];
}
private:
ContextSharedData() : next_sync_point_(1), next_mailbox_(1) {}
unsigned next_sync_point_;
unsigned next_mailbox_;
typedef base::hash_map<unsigned, scoped_refptr<Texture> > TextureMap;
TextureMap textures_;
base::hash_map<unsigned, unsigned> sync_point_for_mailbox_;
};
class ResourceProviderContext : public TestWebGraphicsContext3D {
public:
static scoped_ptr<ResourceProviderContext> Create(
ContextSharedData* shared_data) {
return make_scoped_ptr(
new ResourceProviderContext(Attributes(), shared_data));
}
virtual unsigned insertSyncPoint() OVERRIDE {
unsigned sync_point = shared_data_->InsertSyncPoint();
// Commit the produceTextureCHROMIUM calls at this point, so that
// they're associated with the sync point.
for (PendingProduceTextureList::iterator it =
pending_produce_textures_.begin();
it != pending_produce_textures_.end();
++it) {
shared_data_->ProduceTexture(
(*it)->mailbox, sync_point, (*it)->texture);
}
pending_produce_textures_.clear();
return sync_point;
}
virtual void waitSyncPoint(unsigned sync_point) OVERRIDE {
last_waited_sync_point_ = std::max(sync_point, last_waited_sync_point_);
}
virtual void bindTexture(WGC3Denum target, WebGLId texture) OVERRIDE {
ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
ASSERT_TRUE(!texture || textures_.find(texture) != textures_.end());
current_texture_ = texture;
}
virtual WebGLId createTexture() OVERRIDE {
WebGLId id = TestWebGraphicsContext3D::createTexture();
textures_[id] = new Texture;
return id;
}
virtual void deleteTexture(WebGLId id) OVERRIDE {
TextureMap::iterator it = textures_.find(id);
ASSERT_FALSE(it == textures_.end());
textures_.erase(it);
if (current_texture_ == id)
current_texture_ = 0;
}
virtual void texStorage2DEXT(WGC3Denum target,
WGC3Dint levels,
WGC3Duint internalformat,
WGC3Dint width,
WGC3Dint height) OVERRIDE {
ASSERT_TRUE(current_texture_);
ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
ASSERT_EQ(1, levels);
WGC3Denum 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);
}
virtual void texImage2D(WGC3Denum target,
WGC3Dint level,
WGC3Denum internalformat,
WGC3Dsizei width,
WGC3Dsizei height,
WGC3Dint border,
WGC3Denum format,
WGC3Denum type,
const void* pixels) OVERRIDE {
ASSERT_TRUE(current_texture_);
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);
}
virtual void texSubImage2D(WGC3Denum target,
WGC3Dint level,
WGC3Dint xoffset,
WGC3Dint yoffset,
WGC3Dsizei width,
WGC3Dsizei height,
WGC3Denum format,
WGC3Denum type,
const void* pixels) OVERRIDE {
ASSERT_TRUE(current_texture_);
ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
ASSERT_FALSE(level);
ASSERT_TRUE(textures_[current_texture_].get());
ASSERT_EQ(textures_[current_texture_]->format, format);
ASSERT_EQ(static_cast<unsigned>(GL_UNSIGNED_BYTE), type);
ASSERT_TRUE(pixels);
SetPixels(xoffset, yoffset, width, height, pixels);
}
virtual void texParameteri(WGC3Denum target, WGC3Denum param, WGC3Dint value)
OVERRIDE {
ASSERT_TRUE(current_texture_);
ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
scoped_refptr<Texture> texture = textures_[current_texture_];
ASSERT_TRUE(texture.get());
if (param != GL_TEXTURE_MIN_FILTER)
return;
texture->filter = value;
}
virtual void genMailboxCHROMIUM(WGC3Dbyte* mailbox) OVERRIDE {
return shared_data_->GenMailbox(mailbox);
}
virtual void produceTextureCHROMIUM(WGC3Denum target,
const WGC3Dbyte* mailbox) OVERRIDE {
ASSERT_TRUE(current_texture_);
ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
// Delay moving the texture into the mailbox until the next
// InsertSyncPoint, so that it is not visible to other contexts that
// haven't waited on that sync point.
scoped_ptr<PendingProduceTexture> pending(new PendingProduceTexture);
memcpy(pending->mailbox, mailbox, sizeof(pending->mailbox));
pending->texture = textures_[current_texture_];
pending_produce_textures_.push_back(pending.Pass());
}
virtual void consumeTextureCHROMIUM(WGC3Denum target,
const WGC3Dbyte* mailbox) OVERRIDE {
ASSERT_TRUE(current_texture_);
ASSERT_EQ(static_cast<unsigned>(GL_TEXTURE_2D), target);
textures_[current_texture_] = shared_data_->ConsumeTexture(
mailbox, last_waited_sync_point_);
}
void GetPixels(gfx::Size size, WGC3Denum format, uint8_t* pixels) {
ASSERT_TRUE(current_texture_);
scoped_refptr<Texture> texture = textures_[current_texture_];
ASSERT_TRUE(texture.get());
ASSERT_EQ(texture->size, size);
ASSERT_EQ(texture->format, format);
memcpy(pixels, texture->data.get(), TextureSize(size, format));
}
WGC3Denum GetTextureFilter() {
DCHECK(current_texture_);
scoped_refptr<Texture> texture = textures_[current_texture_];
DCHECK(texture.get());
return texture->filter;
}
int texture_count() { return textures_.size(); }
protected:
ResourceProviderContext(const Attributes& attrs,
ContextSharedData* shared_data)
: TestWebGraphicsContext3D(attrs),
shared_data_(shared_data),
current_texture_(0),
last_waited_sync_point_(0) {}
private:
void AllocateTexture(gfx::Size size, WGC3Denum format) {
ASSERT_TRUE(current_texture_);
scoped_refptr<Texture> texture = textures_[current_texture_];
ASSERT_TRUE(texture.get());
texture->Reallocate(size, format);
}
void SetPixels(int xoffset,
int yoffset,
int width,
int height,
const void* pixels) {
ASSERT_TRUE(current_texture_);
scoped_refptr<Texture> texture = textures_[current_texture_];
ASSERT_TRUE(texture.get());
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 = TextureSize(gfx::Size(width, 1), texture->format);
size_t out_pitch =
TextureSize(gfx::Size(texture->size.width(), 1), texture->format);
uint8_t* dest = texture->data.get() + yoffset * out_pitch +
TextureSize(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;
}
}
typedef base::hash_map<WebGLId, scoped_refptr<Texture> > TextureMap;
struct PendingProduceTexture {
WGC3Dbyte mailbox[64];
scoped_refptr<Texture> texture;
};
typedef ScopedPtrDeque<PendingProduceTexture> PendingProduceTextureList;
ContextSharedData* shared_data_;
WebGLId current_texture_;
TextureMap textures_;
unsigned last_waited_sync_point_;
PendingProduceTextureList pending_produce_textures_;
};
void GetResourcePixels(ResourceProvider* resource_provider,
ResourceProviderContext* context,
ResourceProvider::ResourceId id,
gfx::Size size,
WGC3Denum format,
uint8_t* pixels) {
switch (resource_provider->default_resource_type()) {
case ResourceProvider::GLTexture: {
ResourceProvider::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);
break;
}
case ResourceProvider::Bitmap: {
ResourceProvider::ScopedReadLockSoftware lock_software(resource_provider,
id);
memcpy(pixels,
lock_software.sk_bitmap()->getPixels(),
lock_software.sk_bitmap()->getSize());
break;
}
case ResourceProvider::InvalidType:
NOTREACHED();
break;
}
}
class ResourceProviderTest
: public testing::TestWithParam<ResourceProvider::ResourceType> {
public:
ResourceProviderTest()
: shared_data_(ContextSharedData::Create()) {
switch (GetParam()) {
case ResourceProvider::GLTexture:
output_surface_ =
FakeOutputSurface::Create3d(ResourceProviderContext::Create(
shared_data_.get()).PassAs<WebKit::WebGraphicsContext3D>());
break;
case ResourceProvider::Bitmap:
output_surface_ = FakeOutputSurface::CreateSoftware(
make_scoped_ptr(new SoftwareOutputDevice));
break;
case ResourceProvider::InvalidType:
NOTREACHED();
break;
}
resource_provider_ = ResourceProvider::Create(output_surface_.get(), 0);
}
ResourceProviderContext* context() {
return static_cast<ResourceProviderContext*>(output_surface_->context3d());
}
void SetResourceFilter(ResourceProvider* resource_provider,
ResourceProvider::ResourceId id,
WGC3Denum filter) {
ResourceProvider::ScopedSamplerGL sampler(
resource_provider, id, GL_TEXTURE_2D, filter);
}
WGC3Denum GetResourceFilter(ResourceProvider* resource_provider,
ResourceProvider::ResourceId id) {
DCHECK_EQ(GetParam(), ResourceProvider::GLTexture);
ResourceProvider::ScopedReadLockGL lock_gl(resource_provider, id);
EXPECT_NE(0u, lock_gl.texture_id());
ResourceProviderContext* context = static_cast<ResourceProviderContext*>(
resource_provider->GraphicsContext3D());
context->bindTexture(GL_TEXTURE_2D, lock_gl.texture_id());
return context->GetTextureFilter();
}
protected:
scoped_ptr<ContextSharedData> shared_data_;
scoped_ptr<OutputSurface> output_surface_;
scoped_ptr<ResourceProvider> resource_provider_;
};
void CheckCreateResource(ResourceProvider::ResourceType expected_default_type,
ResourceProvider* resource_provider,
ResourceProviderContext* context) {
DCHECK_EQ(expected_default_type, resource_provider->default_resource_type());
gfx::Size size(1, 1);
WGC3Denum format = GL_RGBA;
size_t pixel_size = TextureSize(size, format);
ASSERT_EQ(4U, pixel_size);
ResourceProvider::ResourceId id = resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
EXPECT_EQ(1, static_cast<int>(resource_provider->num_resources()));
if (expected_default_type == ResourceProvider::GLTexture)
EXPECT_EQ(0, context->texture_count());
uint8_t data[4] = { 1, 2, 3, 4 };
gfx::Rect rect(size);
resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
if (expected_default_type == ResourceProvider::GLTexture)
EXPECT_EQ(1, context->texture_count());
uint8_t result[4] = { 0 };
GetResourcePixels(resource_provider, context, id, size, format, result);
EXPECT_EQ(0, memcmp(data, result, pixel_size));
resource_provider->DeleteResource(id);
EXPECT_EQ(0, static_cast<int>(resource_provider->num_resources()));
if (expected_default_type == ResourceProvider::GLTexture)
EXPECT_EQ(0, context->texture_count());
}
TEST_P(ResourceProviderTest, Basic) {
CheckCreateResource(GetParam(), resource_provider_.get(), context());
}
TEST_P(ResourceProviderTest, Upload) {
gfx::Size size(2, 2);
WGC3Denum format = GL_RGBA;
size_t pixel_size = TextureSize(size, format);
ASSERT_EQ(16U, pixel_size);
ResourceProvider::ResourceId id = resource_provider_->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
uint8_t image[16] = { 0 };
gfx::Rect image_rect(size);
resource_provider_->SetPixels(
id, image, image_rect, image_rect, gfx::Vector2d());
for (uint8_t i = 0; i < pixel_size; ++i)
image[i] = i;
uint8_t result[16] = { 0 };
{
gfx::Rect source_rect(0, 0, 1, 1);
gfx::Vector2d dest_offset(0, 0);
resource_provider_->SetPixels(
id, image, image_rect, source_rect, dest_offset);
uint8_t expected[16] = { 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
GetResourcePixels(
resource_provider_.get(), context(), id, size, format, result);
EXPECT_EQ(0, memcmp(expected, result, pixel_size));
}
{
gfx::Rect source_rect(0, 0, 1, 1);
gfx::Vector2d dest_offset(1, 1);
resource_provider_->SetPixels(
id, image, image_rect, source_rect, dest_offset);
uint8_t expected[16] = { 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3 };
GetResourcePixels(
resource_provider_.get(), context(), id, size, format, result);
EXPECT_EQ(0, memcmp(expected, result, pixel_size));
}
{
gfx::Rect source_rect(1, 0, 1, 1);
gfx::Vector2d dest_offset(0, 1);
resource_provider_->SetPixels(
id, image, image_rect, source_rect, dest_offset);
uint8_t expected[16] = { 0, 1, 2, 3, 0, 0, 0, 0, 4, 5, 6, 7, 0, 1, 2, 3 };
GetResourcePixels(
resource_provider_.get(), context(), id, size, format, result);
EXPECT_EQ(0, memcmp(expected, result, pixel_size));
}
{
gfx::Rect offset_image_rect(gfx::Point(100, 100), size);
gfx::Rect source_rect(100, 100, 1, 1);
gfx::Vector2d dest_offset(1, 0);
resource_provider_->SetPixels(
id, image, offset_image_rect, source_rect, dest_offset);
uint8_t expected[16] = { 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3 };
GetResourcePixels(
resource_provider_.get(), context(), id, size, format, result);
EXPECT_EQ(0, memcmp(expected, result, pixel_size));
}
resource_provider_->DeleteResource(id);
}
TEST_P(ResourceProviderTest, TransferResources) {
// Resource transfer is only supported with GL textures for now.
if (GetParam() != ResourceProvider::GLTexture)
return;
scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
ResourceProviderContext::Create(shared_data_.get())
.PassAs<WebKit::WebGraphicsContext3D>()));
scoped_ptr<ResourceProvider> child_resource_provider(
ResourceProvider::Create(child_output_surface.get(), 0));
gfx::Size size(1, 1);
WGC3Denum format = GL_RGBA;
size_t pixel_size = TextureSize(size, format);
ASSERT_EQ(4U, pixel_size);
ResourceProvider::ResourceId id1 = child_resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
uint8_t data1[4] = { 1, 2, 3, 4 };
gfx::Rect rect(size);
child_resource_provider->SetPixels(id1, data1, rect, rect, gfx::Vector2d());
ResourceProvider::ResourceId id2 = child_resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
uint8_t data2[4] = { 5, 5, 5, 5 };
child_resource_provider->SetPixels(id2, data2, rect, rect, gfx::Vector2d());
int child_id = resource_provider_->CreateChild();
{
// 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);
TransferableResourceArray list;
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(2u, list.size());
EXPECT_NE(0u, list[0].sync_point);
EXPECT_NE(0u, list[1].sync_point);
EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2));
resource_provider_->ReceiveFromChild(child_id, list);
}
EXPECT_EQ(2u, resource_provider_->num_resources());
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
ResourceProvider::ResourceId mapped_id1 = resource_map[id1];
ResourceProvider::ResourceId mapped_id2 = resource_map[id2];
EXPECT_NE(0u, mapped_id1);
EXPECT_NE(0u, mapped_id2);
EXPECT_FALSE(resource_provider_->InUseByConsumer(id1));
EXPECT_FALSE(resource_provider_->InUseByConsumer(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 is a noop.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id1);
TransferableResourceArray list;
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&list);
EXPECT_EQ(0u, list.size());
}
{
// Transfer resources back from the parent to the child.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(mapped_id1);
resource_ids_to_transfer.push_back(mapped_id2);
TransferableResourceArray list;
resource_provider_->PrepareSendToChild(
child_id, resource_ids_to_transfer, &list);
ASSERT_EQ(2u, list.size());
EXPECT_NE(0u, list[0].sync_point);
EXPECT_NE(0u, list[1].sync_point);
child_resource_provider->ReceiveFromParent(list);
}
EXPECT_FALSE(child_resource_provider->InUseByConsumer(id1));
EXPECT_FALSE(child_resource_provider->InUseByConsumer(id2));
ResourceProviderContext* child_context =
static_cast<ResourceProviderContext*>(child_output_surface->context3d());
{
ResourceProvider::ScopedReadLockGL lock(child_resource_provider.get(), id1);
ASSERT_NE(0U, lock.texture_id());
child_context->bindTexture(GL_TEXTURE_2D, lock.texture_id());
child_context->GetPixels(size, format, result);
EXPECT_EQ(0, memcmp(data1, result, pixel_size));
}
{
ResourceProvider::ScopedReadLockGL lock(child_resource_provider.get(), id2);
ASSERT_NE(0U, lock.texture_id());
child_context->bindTexture(GL_TEXTURE_2D, lock.texture_id());
child_context->GetPixels(size, format, result);
EXPECT_EQ(0, memcmp(data2, result, 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);
TransferableResourceArray list;
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(2u, list.size());
EXPECT_NE(0u, list[0].sync_point);
EXPECT_NE(0u, list[1].sync_point);
EXPECT_TRUE(child_resource_provider->InUseByConsumer(id1));
EXPECT_TRUE(child_resource_provider->InUseByConsumer(id2));
resource_provider_->ReceiveFromChild(child_id, list);
}
EXPECT_EQ(2u, resource_provider_->num_resources());
resource_provider_->DestroyChild(child_id);
EXPECT_EQ(0u, resource_provider_->num_resources());
}
TEST_P(ResourceProviderTest, DeleteTransferredResources) {
// Resource transfer is only supported with GL textures for now.
if (GetParam() != ResourceProvider::GLTexture)
return;
scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
ResourceProviderContext::Create(shared_data_.get())
.PassAs<WebKit::WebGraphicsContext3D>()));
scoped_ptr<ResourceProvider> child_resource_provider(
ResourceProvider::Create(child_output_surface.get(), 0));
gfx::Size size(1, 1);
WGC3Denum format = GL_RGBA;
size_t pixel_size = TextureSize(size, format);
ASSERT_EQ(4U, pixel_size);
ResourceProvider::ResourceId id = child_resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
uint8_t data[4] = { 1, 2, 3, 4 };
gfx::Rect rect(size);
child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
int child_id = resource_provider_->CreateChild();
{
// Transfer some resource to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id);
TransferableResourceArray list;
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(1u, list.size());
EXPECT_NE(0u, list[0].sync_point);
EXPECT_TRUE(child_resource_provider->InUseByConsumer(id));
resource_provider_->ReceiveFromChild(child_id, list);
}
// Delete textures in the child, while they are transfered.
child_resource_provider->DeleteResource(id);
EXPECT_EQ(1u, child_resource_provider->num_resources());
{
// Transfer resources back from the parent to the child.
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
ResourceProvider::ResourceId mapped_id = resource_map[id];
EXPECT_NE(0u, mapped_id);
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(mapped_id);
TransferableResourceArray list;
resource_provider_->PrepareSendToChild(
child_id, resource_ids_to_transfer, &list);
ASSERT_EQ(1u, list.size());
EXPECT_NE(0u, list[0].sync_point);
child_resource_provider->ReceiveFromParent(list);
}
EXPECT_EQ(0u, child_resource_provider->num_resources());
}
TEST_P(ResourceProviderTest, TextureFilters) {
// Resource transfer is only supported with GL textures for now.
if (GetParam() != ResourceProvider::GLTexture)
return;
scoped_ptr<OutputSurface> child_output_surface(FakeOutputSurface::Create3d(
ResourceProviderContext::Create(shared_data_.get())
.PassAs<WebKit::WebGraphicsContext3D>()));
scoped_ptr<ResourceProvider> child_resource_provider(
ResourceProvider::Create(child_output_surface.get(), 0));
gfx::Size size(1, 1);
WGC3Denum format = GL_RGBA;
size_t pixel_size = TextureSize(size, format);
ASSERT_EQ(4U, pixel_size);
ResourceProvider::ResourceId id = child_resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
uint8_t data[4] = { 1, 2, 3, 4 };
gfx::Rect rect(size);
child_resource_provider->SetPixels(id, data, rect, rect, gfx::Vector2d());
EXPECT_EQ(static_cast<unsigned>(GL_LINEAR),
GetResourceFilter(child_resource_provider.get(), id));
SetResourceFilter(child_resource_provider.get(), id, GL_NEAREST);
EXPECT_EQ(static_cast<unsigned>(GL_NEAREST),
GetResourceFilter(child_resource_provider.get(), id));
int child_id = resource_provider_->CreateChild();
{
// Transfer some resource to the parent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(id);
TransferableResourceArray list;
child_resource_provider->PrepareSendToParent(resource_ids_to_transfer,
&list);
ASSERT_EQ(1u, list.size());
EXPECT_EQ(static_cast<unsigned>(GL_NEAREST), list[0].filter);
resource_provider_->ReceiveFromChild(child_id, list);
}
ResourceProvider::ResourceIdMap resource_map =
resource_provider_->GetChildToParentMap(child_id);
ResourceProvider::ResourceId mapped_id = resource_map[id];
EXPECT_NE(0u, mapped_id);
EXPECT_EQ(static_cast<unsigned>(GL_NEAREST),
GetResourceFilter(resource_provider_.get(), mapped_id));
SetResourceFilter(resource_provider_.get(), mapped_id, GL_LINEAR);
EXPECT_EQ(static_cast<unsigned>(GL_LINEAR),
GetResourceFilter(resource_provider_.get(), mapped_id));
{
// Transfer resources back from the parent to the child.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(mapped_id);
TransferableResourceArray list;
resource_provider_->PrepareSendToChild(
child_id, resource_ids_to_transfer, &list);
ASSERT_EQ(1u, list.size());
EXPECT_EQ(static_cast<unsigned>(GL_LINEAR), list[0].filter);
child_resource_provider->ReceiveFromParent(list);
}
EXPECT_EQ(static_cast<unsigned>(GL_LINEAR),
GetResourceFilter(child_resource_provider.get(), id));
SetResourceFilter(child_resource_provider.get(), id, GL_NEAREST);
EXPECT_EQ(static_cast<unsigned>(GL_NEAREST),
GetResourceFilter(child_resource_provider.get(), id));
}
void ReleaseTextureMailbox(unsigned* release_sync_point,
bool* release_lost_resource,
unsigned sync_point,
bool lost_resource) {
*release_sync_point = sync_point;
*release_lost_resource = lost_resource;
}
TEST_P(ResourceProviderTest, TransferMailboxResources) {
// Resource transfer is only supported with GL textures for now.
if (GetParam() != ResourceProvider::GLTexture)
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()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
unsigned sync_point = context()->insertSyncPoint();
// All the logic below assumes that the sync points are all positive.
EXPECT_LT(0u, sync_point);
unsigned release_sync_point = 0;
bool lost_resource = false;
TextureMailbox::ReleaseCallback callback =
base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
ResourceProvider::ResourceId resource =
resource_provider_->CreateResourceFromTextureMailbox(
TextureMailbox(mailbox, callback, sync_point));
EXPECT_EQ(1, context()->texture_count());
EXPECT_EQ(0u, release_sync_point);
{
// Transfer the resource, expect the sync points to be consistent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(resource);
TransferableResourceArray list;
resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
ASSERT_EQ(1u, list.size());
EXPECT_LE(sync_point, list[0].sync_point);
EXPECT_EQ(0,
memcmp(mailbox.name, list[0].mailbox.name, sizeof(mailbox.name)));
EXPECT_EQ(0u, release_sync_point);
context()->waitSyncPoint(list[0].sync_point);
unsigned other_texture = context()->createTexture();
context()->bindTexture(GL_TEXTURE_2D, other_texture);
context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
uint8_t test_data[4] = { 0 };
context()->GetPixels(gfx::Size(1, 1), GL_RGBA, test_data);
EXPECT_EQ(0, memcmp(data, test_data, sizeof(data)));
context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
context()->deleteTexture(other_texture);
list[0].sync_point = context()->insertSyncPoint();
EXPECT_LT(0u, list[0].sync_point);
// Receive the resource, then delete it, expect the sync points to be
// consistent.
resource_provider_->ReceiveFromParent(list);
EXPECT_EQ(1, context()->texture_count());
EXPECT_EQ(0u, release_sync_point);
resource_provider_->DeleteResource(resource);
EXPECT_LE(list[0].sync_point, release_sync_point);
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_point = release_sync_point;
EXPECT_LT(0u, sync_point);
release_sync_point = 0;
resource = resource_provider_->CreateResourceFromTextureMailbox(
TextureMailbox(mailbox, callback, sync_point));
EXPECT_EQ(1, context()->texture_count());
EXPECT_EQ(0u, release_sync_point);
{
// Transfer the resource, expect the sync points to be consistent.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(resource);
TransferableResourceArray list;
resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
ASSERT_EQ(1u, list.size());
EXPECT_LE(sync_point, list[0].sync_point);
EXPECT_EQ(0,
memcmp(mailbox.name, list[0].mailbox.name, sizeof(mailbox.name)));
EXPECT_EQ(0u, release_sync_point);
context()->waitSyncPoint(list[0].sync_point);
unsigned other_texture = context()->createTexture();
context()->bindTexture(GL_TEXTURE_2D, other_texture);
context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
uint8_t test_data[4] = { 0 };
context()->GetPixels(gfx::Size(1, 1), GL_RGBA, test_data);
EXPECT_EQ(0, memcmp(data, test_data, sizeof(data)));
context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
context()->deleteTexture(other_texture);
list[0].sync_point = context()->insertSyncPoint();
EXPECT_LT(0u, list[0].sync_point);
// Delete the resource, which shouldn't do anything.
resource_provider_->DeleteResource(resource);
EXPECT_EQ(1, context()->texture_count());
EXPECT_EQ(0u, release_sync_point);
// Then receive the resource which should release the mailbox, expect the
// sync points to be consistent.
resource_provider_->ReceiveFromParent(list);
EXPECT_LE(list[0].sync_point, release_sync_point);
EXPECT_FALSE(lost_resource);
}
context()->waitSyncPoint(release_sync_point);
context()->bindTexture(GL_TEXTURE_2D, texture);
context()->consumeTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
context()->deleteTexture(texture);
}
TEST_P(ResourceProviderTest, Shutdown) {
// TextureMailbox callbacks only exist for GL textures for now.
if (GetParam() != ResourceProvider::GLTexture)
return;
unsigned texture = context()->createTexture();
context()->bindTexture(GL_TEXTURE_2D, texture);
gpu::Mailbox mailbox;
context()->genMailboxCHROMIUM(mailbox.name);
context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
unsigned sync_point = context()->insertSyncPoint();
EXPECT_LT(0u, sync_point);
unsigned release_sync_point = 0;
bool lost_resource = false;
TextureMailbox::ReleaseCallback callback =
base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
resource_provider_->CreateResourceFromTextureMailbox(
TextureMailbox(mailbox, callback, sync_point));
EXPECT_EQ(0u, release_sync_point);
EXPECT_FALSE(lost_resource);
resource_provider_.reset();
EXPECT_LE(sync_point, release_sync_point);
EXPECT_FALSE(lost_resource);
}
static scoped_ptr<base::SharedMemory> CreateAndFillSharedMemory(
gfx::Size size, uint32_t value) {
scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory);
CHECK(shared_memory->CreateAndMapAnonymous(4 * size.GetArea()));
uint32_t* pixels = reinterpret_cast<uint32_t*>(shared_memory->memory());
CHECK(pixels);
std::fill_n(pixels, size.GetArea(), value);
return shared_memory.Pass();
}
static void ReleaseSharedMemoryCallback(
bool* release_called,
unsigned sync_point, bool lost_resource) {
*release_called = true;
}
TEST_P(ResourceProviderTest, ShutdownSharedMemory) {
if (GetParam() != ResourceProvider::Bitmap)
return;
gfx::Size size(64, 64);
scoped_ptr<base::SharedMemory> shared_memory(
CreateAndFillSharedMemory(size, 0));
bool release_called = false;
TextureMailbox::ReleaseCallback callback =
base::Bind(ReleaseSharedMemoryCallback, &release_called);
resource_provider_->CreateResourceFromTextureMailbox(
TextureMailbox(shared_memory.get(), size, callback));
resource_provider_.reset();
EXPECT_TRUE(release_called);
}
TEST_P(ResourceProviderTest, ShutdownWithExportedResource) {
// TextureMailbox callbacks only exist for GL textures for now.
if (GetParam() != ResourceProvider::GLTexture)
return;
unsigned texture = context()->createTexture();
context()->bindTexture(GL_TEXTURE_2D, texture);
gpu::Mailbox mailbox;
context()->genMailboxCHROMIUM(mailbox.name);
context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
unsigned sync_point = context()->insertSyncPoint();
EXPECT_LT(0u, sync_point);
unsigned release_sync_point = 0;
bool lost_resource = false;
TextureMailbox::ReleaseCallback callback =
base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
ResourceProvider::ResourceId resource =
resource_provider_->CreateResourceFromTextureMailbox(
TextureMailbox(mailbox, callback, sync_point));
// Transfer the resource, so we can't release it properly on shutdown.
ResourceProvider::ResourceIdArray resource_ids_to_transfer;
resource_ids_to_transfer.push_back(resource);
TransferableResourceArray list;
resource_provider_->PrepareSendToParent(resource_ids_to_transfer, &list);
EXPECT_EQ(0u, release_sync_point);
EXPECT_FALSE(lost_resource);
resource_provider_.reset();
// Since the resource is in the parent, the child considers it lost.
EXPECT_EQ(0u, release_sync_point);
EXPECT_TRUE(lost_resource);
}
TEST_P(ResourceProviderTest, LostContext) {
// TextureMailbox callbacks only exist for GL textures for now.
if (GetParam() != ResourceProvider::GLTexture)
return;
unsigned texture = context()->createTexture();
context()->bindTexture(GL_TEXTURE_2D, texture);
gpu::Mailbox mailbox;
context()->genMailboxCHROMIUM(mailbox.name);
context()->produceTextureCHROMIUM(GL_TEXTURE_2D, mailbox.name);
unsigned sync_point = context()->insertSyncPoint();
EXPECT_LT(0u, sync_point);
unsigned release_sync_point = 0;
bool lost_resource = false;
TextureMailbox::ReleaseCallback callback =
base::Bind(ReleaseTextureMailbox, &release_sync_point, &lost_resource);
resource_provider_->CreateResourceFromTextureMailbox(
TextureMailbox(mailbox, callback, sync_point));
EXPECT_EQ(0u, release_sync_point);
EXPECT_FALSE(lost_resource);
resource_provider_->DidLoseOutputSurface();
resource_provider_.reset();
EXPECT_LE(sync_point, release_sync_point);
EXPECT_TRUE(lost_resource);
}
class TextureStateTrackingContext : public TestWebGraphicsContext3D {
public:
MOCK_METHOD2(bindTexture, void(WGC3Denum target, WebGLId texture));
MOCK_METHOD3(texParameteri,
void(WGC3Denum target, WGC3Denum pname, WGC3Dint param));
MOCK_METHOD1(waitSyncPoint, void(unsigned sync_point));
MOCK_METHOD0(insertSyncPoint, unsigned(void));
MOCK_METHOD2(produceTextureCHROMIUM, void(WGC3Denum target,
const WGC3Dbyte* mailbox));
MOCK_METHOD2(consumeTextureCHROMIUM, void(WGC3Denum target,
const WGC3Dbyte* mailbox));
// Force all textures to be "1" so we can test for them.
virtual WebKit::WebGLId NextTextureId() OVERRIDE { return 1; }
};
TEST_P(ResourceProviderTest, ScopedSampler) {
// Sampling is only supported for GL textures.
if (GetParam() != ResourceProvider::GLTexture)
return;
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
new TextureStateTrackingContext)));
TextureStateTrackingContext* context =
static_cast<TextureStateTrackingContext*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
gfx::Size size(1, 1);
WGC3Denum format = GL_RGBA;
int texture_id = 1;
// Check that the texture gets created with the right sampler settings.
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id))
.Times(2); // Once to create and once to allocate.
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));
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D,
GL_TEXTURE_POOL_CHROMIUM,
GL_TEXTURE_POOL_UNMANAGED_CHROMIUM));
ResourceProvider::ResourceId id = resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
resource_provider->AllocateForTesting(id);
// Creating a sampler with the default filter should not change any texture
// parameters.
{
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id));
ResourceProvider::ScopedSamplerGL sampler(
resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
}
// Using a different filter should be reflected in the texture parameters.
{
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, 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));
ResourceProvider::ScopedSamplerGL sampler(
resource_provider.get(), id, GL_TEXTURE_2D, GL_NEAREST);
}
// Test resetting to the default filter.
{
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));
ResourceProvider::ScopedSamplerGL sampler(
resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
}
Mock::VerifyAndClearExpectations(context);
}
TEST_P(ResourceProviderTest, ManagedResource) {
// Sampling is only supported for GL textures.
if (GetParam() != ResourceProvider::GLTexture)
return;
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
new TextureStateTrackingContext)));
TextureStateTrackingContext* context =
static_cast<TextureStateTrackingContext*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
gfx::Size size(1, 1);
WGC3Denum format = GL_RGBA;
int texture_id = 1;
// Check that the texture gets created with the right sampler settings.
ResourceProvider::ResourceId id = resource_provider->CreateManagedResource(
size, format, ResourceProvider::TextureUsageAny);
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));
EXPECT_CALL(*context,
texParameteri(GL_TEXTURE_2D,
GL_TEXTURE_POOL_CHROMIUM,
GL_TEXTURE_POOL_MANAGED_CHROMIUM));
resource_provider->CreateForTesting(id);
EXPECT_NE(0u, id);
Mock::VerifyAndClearExpectations(context);
}
static void EmptyReleaseCallback(unsigned sync_point, bool lost_resource) {}
TEST_P(ResourceProviderTest, TextureMailbox_SharedMemory) {
if (GetParam() != ResourceProvider::Bitmap)
return;
gfx::Size size(64, 64);
const uint32_t kBadBeef = 0xbadbeef;
scoped_ptr<base::SharedMemory> shared_memory(
CreateAndFillSharedMemory(size, kBadBeef));
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::CreateSoftware(make_scoped_ptr(
new SoftwareOutputDevice)));
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback);
TextureMailbox mailbox(shared_memory.get(), size, callback);
ResourceProvider::ResourceId id =
resource_provider->CreateResourceFromTextureMailbox(mailbox);
EXPECT_NE(0u, id);
{
ResourceProvider::ScopedReadLockSoftware lock(resource_provider.get(), 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);
}
}
TEST_P(ResourceProviderTest, TextureMailbox_GLTexture2D) {
// Mailboxing is only supported for GL textures.
if (GetParam() != ResourceProvider::GLTexture)
return;
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
new TextureStateTrackingContext)));
TextureStateTrackingContext* context =
static_cast<TextureStateTrackingContext*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
unsigned texture_id = 1;
unsigned sync_point = 30;
unsigned target = GL_TEXTURE_2D;
EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
EXPECT_CALL(*context, waitSyncPoint(_)).Times(0);
EXPECT_CALL(*context, insertSyncPoint()).Times(0);
EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*context, consumeTextureCHROMIUM(_, _)).Times(0);
gpu::Mailbox gpu_mailbox;
memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback);
TextureMailbox mailbox(gpu_mailbox,
callback,
sync_point);
ResourceProvider::ResourceId id =
resource_provider->CreateResourceFromTextureMailbox(mailbox);
EXPECT_NE(0u, id);
Mock::VerifyAndClearExpectations(context);
{
// Using the texture does a consume of the mailbox.
EXPECT_CALL(*context, bindTexture(target, texture_id));
EXPECT_CALL(*context, waitSyncPoint(sync_point));
EXPECT_CALL(*context, consumeTextureCHROMIUM(target, _));
EXPECT_CALL(*context, insertSyncPoint()).Times(0);
EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
ResourceProvider::ScopedReadLockGL lock(resource_provider.get(), 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, insertSyncPoint());
EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*context, waitSyncPoint(_)).Times(0);
EXPECT_CALL(*context, consumeTextureCHROMIUM(_, _)).Times(0);
}
}
TEST_P(ResourceProviderTest, TextureMailbox_GLTextureExternalOES) {
// Mailboxing is only supported for GL textures.
if (GetParam() != ResourceProvider::GLTexture)
return;
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(
new TextureStateTrackingContext)));
TextureStateTrackingContext* context =
static_cast<TextureStateTrackingContext*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
unsigned texture_id = 1;
unsigned sync_point = 30;
unsigned target = GL_TEXTURE_EXTERNAL_OES;
EXPECT_CALL(*context, bindTexture(_, _)).Times(0);
EXPECT_CALL(*context, waitSyncPoint(_)).Times(0);
EXPECT_CALL(*context, insertSyncPoint()).Times(0);
EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*context, consumeTextureCHROMIUM(_, _)).Times(0);
gpu::Mailbox gpu_mailbox;
memcpy(gpu_mailbox.name, "Hello world", strlen("Hello world") + 1);
TextureMailbox::ReleaseCallback callback = base::Bind(&EmptyReleaseCallback);
TextureMailbox mailbox(gpu_mailbox,
callback,
target,
sync_point);
ResourceProvider::ResourceId id =
resource_provider->CreateResourceFromTextureMailbox(mailbox);
EXPECT_NE(0u, id);
Mock::VerifyAndClearExpectations(context);
{
// Using the texture does a consume of the mailbox.
EXPECT_CALL(*context, bindTexture(target, texture_id));
EXPECT_CALL(*context, waitSyncPoint(sync_point));
EXPECT_CALL(*context, consumeTextureCHROMIUM(target, _));
EXPECT_CALL(*context, insertSyncPoint()).Times(0);
EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
ResourceProvider::ScopedReadLockGL lock(resource_provider.get(), 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, insertSyncPoint());
EXPECT_CALL(*context, produceTextureCHROMIUM(_, _)).Times(0);
EXPECT_CALL(*context, waitSyncPoint(_)).Times(0);
EXPECT_CALL(*context, consumeTextureCHROMIUM(_, _)).Times(0);
}
}
class AllocationTrackingContext3D : public TestWebGraphicsContext3D {
public:
MOCK_METHOD0(createTexture, WebGLId());
MOCK_METHOD1(deleteTexture, void(WebGLId texture_id));
MOCK_METHOD2(bindTexture, void(WGC3Denum target, WebGLId texture));
MOCK_METHOD9(texImage2D,
void(WGC3Denum target,
WGC3Dint level,
WGC3Denum internalformat,
WGC3Dsizei width,
WGC3Dsizei height,
WGC3Dint border,
WGC3Denum format,
WGC3Denum type,
const void* pixels));
MOCK_METHOD9(texSubImage2D,
void(WGC3Denum target,
WGC3Dint level,
WGC3Dint xoffset,
WGC3Dint yoffset,
WGC3Dsizei width,
WGC3Dsizei height,
WGC3Denum format,
WGC3Denum type,
const void* pixels));
MOCK_METHOD9(asyncTexImage2DCHROMIUM,
void(WGC3Denum target,
WGC3Dint level,
WGC3Denum internalformat,
WGC3Dsizei width,
WGC3Dsizei height,
WGC3Dint border,
WGC3Denum format,
WGC3Denum type,
const void* pixels));
MOCK_METHOD9(asyncTexSubImage2DCHROMIUM,
void(WGC3Denum target,
WGC3Dint level,
WGC3Dint xoffset,
WGC3Dint yoffset,
WGC3Dsizei width,
WGC3Dsizei height,
WGC3Denum format,
WGC3Denum type,
const void* pixels));
MOCK_METHOD1(waitAsyncTexImage2DCHROMIUM, void(WGC3Denum));
MOCK_METHOD3(createImageCHROMIUM, WGC3Duint(WGC3Dsizei, WGC3Dsizei,
WGC3Denum));
MOCK_METHOD1(destroyImageCHROMIUM, void(WGC3Duint));
MOCK_METHOD2(mapImageCHROMIUM, void*(WGC3Duint, WGC3Denum));
MOCK_METHOD3(getImageParameterivCHROMIUM, void(WGC3Duint, WGC3Denum,
GLint*));
MOCK_METHOD1(unmapImageCHROMIUM, void(WGC3Duint));
MOCK_METHOD2(bindTexImage2DCHROMIUM, void(WGC3Denum, WGC3Dint));
MOCK_METHOD2(releaseTexImage2DCHROMIUM, void(WGC3Denum, WGC3Dint));
};
TEST_P(ResourceProviderTest, TextureAllocation) {
// Only for GL textures.
if (GetParam() != ResourceProvider::GLTexture)
return;
scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
static_cast<WebKit::WebGraphicsContext3D*>(
new StrictMock<AllocationTrackingContext3D>));
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::Create3d(mock_context.Pass()));
gfx::Size size(2, 2);
gfx::Vector2d offset(0, 0);
gfx::Rect rect(0, 0, 2, 2);
WGC3Denum format = GL_RGBA;
ResourceProvider::ResourceId id = 0;
uint8_t pixels[16] = { 0 };
int texture_id = 123;
AllocationTrackingContext3D* context =
static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
// Lazy allocation. Don't allocate when creating the resource.
id = resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(1);
resource_provider->CreateForTesting(id);
EXPECT_CALL(*context, deleteTexture(texture_id)).Times(1);
resource_provider->DeleteResource(id);
Mock::VerifyAndClearExpectations(context);
// Do allocate when we set the pixels.
id = resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(3);
EXPECT_CALL(*context, texImage2D(_, _, _, 2, 2, _, _, _, _)).Times(1);
EXPECT_CALL(*context, texSubImage2D(_, _, _, _, 2, 2, _, _, _)).Times(1);
resource_provider->SetPixels(id, pixels, rect, rect, offset);
EXPECT_CALL(*context, deleteTexture(texture_id)).Times(1);
resource_provider->DeleteResource(id);
Mock::VerifyAndClearExpectations(context);
// Same for async version.
id = resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
resource_provider->AcquirePixelBuffer(id);
EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
EXPECT_CALL(*context, asyncTexImage2DCHROMIUM(_, _, _, 2, 2, _, _, _, _))
.Times(1);
resource_provider->BeginSetPixels(id);
ASSERT_TRUE(resource_provider->DidSetPixelsComplete(id));
resource_provider->ReleasePixelBuffer(id);
EXPECT_CALL(*context, deleteTexture(texture_id)).Times(1);
resource_provider->DeleteResource(id);
Mock::VerifyAndClearExpectations(context);
}
TEST_P(ResourceProviderTest, PixelBuffer_GLTexture) {
if (GetParam() != ResourceProvider::GLTexture)
return;
scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
static_cast<WebKit::WebGraphicsContext3D*>(
new StrictMock<AllocationTrackingContext3D>));
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::Create3d(mock_context.Pass()));
gfx::Size size(2, 2);
WGC3Denum format = GL_RGBA;
ResourceProvider::ResourceId id = 0;
int texture_id = 123;
AllocationTrackingContext3D* context =
static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
id = resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
resource_provider->AcquirePixelBuffer(id);
EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
EXPECT_CALL(*context, asyncTexImage2DCHROMIUM(_, _, _, 2, 2, _, _, _, _))
.Times(1);
resource_provider->BeginSetPixels(id);
EXPECT_TRUE(resource_provider->DidSetPixelsComplete(id));
resource_provider->ReleasePixelBuffer(id);
EXPECT_CALL(*context, deleteTexture(texture_id)).Times(1);
resource_provider->DeleteResource(id);
Mock::VerifyAndClearExpectations(context);
}
TEST_P(ResourceProviderTest, PixelBuffer_Bitmap) {
if (GetParam() != ResourceProvider::Bitmap)
return;
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::CreateSoftware(make_scoped_ptr(
new SoftwareOutputDevice)));
gfx::Size size(1, 1);
WGC3Denum format = GL_RGBA;
ResourceProvider::ResourceId id = 0;
const uint32_t kBadBeef = 0xbadbeef;
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
id = resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
resource_provider->AcquirePixelBuffer(id);
void* data = resource_provider->MapPixelBuffer(id);
ASSERT_TRUE(!!data);
memcpy(data, &kBadBeef, sizeof(kBadBeef));
resource_provider->UnmapPixelBuffer(id);
resource_provider->BeginSetPixels(id);
EXPECT_TRUE(resource_provider->DidSetPixelsComplete(id));
resource_provider->ReleasePixelBuffer(id);
{
ResourceProvider::ScopedReadLockSoftware lock(resource_provider.get(), 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(0, 0), kBadBeef);
}
resource_provider->DeleteResource(id);
}
TEST_P(ResourceProviderTest, ForcingAsyncUploadToComplete) {
// Only for GL textures.
if (GetParam() != ResourceProvider::GLTexture)
return;
scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
static_cast<WebKit::WebGraphicsContext3D*>(
new StrictMock<AllocationTrackingContext3D>));
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::Create3d(mock_context.Pass()));
gfx::Size size(2, 2);
WGC3Denum format = GL_RGBA;
ResourceProvider::ResourceId id = 0;
int texture_id = 123;
AllocationTrackingContext3D* context =
static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
id = resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
resource_provider->AcquirePixelBuffer(id);
EXPECT_CALL(*context, createTexture()).WillOnce(Return(texture_id));
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(2);
EXPECT_CALL(*context, asyncTexImage2DCHROMIUM(_, _, _, 2, 2, _, _, _, _))
.Times(1);
resource_provider->BeginSetPixels(id);
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, texture_id)).Times(1);
EXPECT_CALL(*context, waitAsyncTexImage2DCHROMIUM(GL_TEXTURE_2D)).Times(1);
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, 0)).Times(1);
resource_provider->ForceSetPixelsToComplete(id);
resource_provider->ReleasePixelBuffer(id);
EXPECT_CALL(*context, deleteTexture(texture_id)).Times(1);
resource_provider->DeleteResource(id);
Mock::VerifyAndClearExpectations(context);
}
TEST_P(ResourceProviderTest, PixelBufferLostContext) {
scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
static_cast<WebKit::WebGraphicsContext3D*>(
new NiceMock<AllocationTrackingContext3D>));
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::Create3d(mock_context.Pass()));
gfx::Size size(2, 2);
WGC3Denum format = GL_RGBA;
ResourceProvider::ResourceId id = 0;
int texture_id = 123;
AllocationTrackingContext3D* context =
static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
EXPECT_CALL(*context, createTexture()).WillRepeatedly(Return(texture_id));
id = resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
context->loseContextCHROMIUM(GL_GUILTY_CONTEXT_RESET_ARB,
GL_INNOCENT_CONTEXT_RESET_ARB);
resource_provider->AcquirePixelBuffer(id);
uint8_t* buffer = resource_provider->MapPixelBuffer(id);
EXPECT_TRUE(buffer == NULL);
resource_provider->UnmapPixelBuffer(id);
resource_provider->ReleasePixelBuffer(id);
Mock::VerifyAndClearExpectations(context);
}
TEST_P(ResourceProviderTest, Image_GLTexture) {
// Only for GL textures.
if (GetParam() != ResourceProvider::GLTexture)
return;
scoped_ptr<WebKit::WebGraphicsContext3D> mock_context(
static_cast<WebKit::WebGraphicsContext3D*>(
new StrictMock<AllocationTrackingContext3D>));
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::Create3d(mock_context.Pass()));
const int kWidth = 2;
const int kHeight = 2;
gfx::Size size(kWidth, kHeight);
WGC3Denum format = GL_RGBA;
ResourceProvider::ResourceId id = 0;
const unsigned kTextureId = 123u;
const unsigned kImageId = 234u;
AllocationTrackingContext3D* context =
static_cast<AllocationTrackingContext3D*>(output_surface->context3d());
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
id = resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
EXPECT_CALL(*context, createImageCHROMIUM(kWidth, kHeight, GL_RGBA8_OES))
.WillOnce(Return(kImageId))
.RetiresOnSaturation();
resource_provider->AcquireImage(id);
void* dummy_mapped_buffer_address = NULL;
EXPECT_CALL(*context, mapImageCHROMIUM(kImageId, GL_READ_WRITE))
.WillOnce(Return(dummy_mapped_buffer_address))
.RetiresOnSaturation();
resource_provider->MapImage(id);
const int kStride = 4;
EXPECT_CALL(*context, getImageParameterivCHROMIUM(kImageId,
GL_IMAGE_ROWBYTES_CHROMIUM,
_))
.WillOnce(SetArgPointee<2>(kStride))
.RetiresOnSaturation();
int stride = resource_provider->GetImageStride(id);
EXPECT_EQ(kStride, stride);
EXPECT_CALL(*context, unmapImageCHROMIUM(kImageId))
.Times(1)
.RetiresOnSaturation();
resource_provider->UnmapImage(id);
EXPECT_CALL(*context, createTexture())
.WillOnce(Return(kTextureId))
.RetiresOnSaturation();
// Once in CreateTextureId and once in BindForSampling
EXPECT_CALL(*context, bindTexture(GL_TEXTURE_2D, kTextureId)).Times(2)
.RetiresOnSaturation();
EXPECT_CALL(*context, bindTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
.Times(1)
.RetiresOnSaturation();
EXPECT_CALL(*context, releaseTexImage2DCHROMIUM(GL_TEXTURE_2D, kImageId))
.Times(1)
.RetiresOnSaturation();
EXPECT_CALL(*context, deleteTexture(kTextureId))
.Times(1)
.RetiresOnSaturation();
{
ResourceProvider::ScopedSamplerGL lock_gl(
resource_provider.get(), id, GL_TEXTURE_2D, GL_LINEAR);
EXPECT_EQ(kTextureId, lock_gl.texture_id());
}
EXPECT_CALL(*context, destroyImageCHROMIUM(kImageId))
.Times(1)
.RetiresOnSaturation();
resource_provider->ReleaseImage(id);
}
TEST_P(ResourceProviderTest, Image_Bitmap) {
if (GetParam() != ResourceProvider::Bitmap)
return;
scoped_ptr<OutputSurface> output_surface(
FakeOutputSurface::CreateSoftware(make_scoped_ptr(
new SoftwareOutputDevice)));
gfx::Size size(1, 1);
WGC3Denum format = GL_RGBA;
ResourceProvider::ResourceId id = 0;
const uint32_t kBadBeef = 0xbadbeef;
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
id = resource_provider->CreateResource(
size, format, ResourceProvider::TextureUsageAny);
resource_provider->AcquireImage(id);
const int kStride = 0;
int stride = resource_provider->GetImageStride(id);
EXPECT_EQ(kStride, stride);
void* data = resource_provider->MapImage(id);
ASSERT_TRUE(!!data);
memcpy(data, &kBadBeef, sizeof(kBadBeef));
resource_provider->UnmapImage(id);
{
ResourceProvider::ScopedReadLockSoftware lock(resource_provider.get(), 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(0, 0), kBadBeef);
}
resource_provider->ReleaseImage(id);
resource_provider->DeleteResource(id);
}
void InitializeGLAndCheck(ContextSharedData* shared_data,
ResourceProvider* resource_provider,
FakeOutputSurface* output_surface) {
scoped_ptr<ResourceProviderContext> context =
ResourceProviderContext::Create(shared_data);
output_surface->SetAndInitializeContext3D(
context.PassAs<WebKit::WebGraphicsContext3D>());
EXPECT_TRUE(resource_provider->InitializeGL());
CheckCreateResource(
ResourceProvider::GLTexture,
resource_provider,
static_cast<ResourceProviderContext*>(output_surface->context3d()));
}
TEST(ResourceProviderTest, BasicInitializeGLSoftware) {
scoped_ptr<ContextSharedData> shared_data = ContextSharedData::Create();
FakeOutputSurfaceClient client;
scoped_ptr<FakeOutputSurface> output_surface(
FakeOutputSurface::CreateDeferredGL(
scoped_ptr<SoftwareOutputDevice>(new SoftwareOutputDevice)));
EXPECT_TRUE(output_surface->BindToClient(&client));
scoped_ptr<ResourceProvider> resource_provider(
ResourceProvider::Create(output_surface.get(), 0));
CheckCreateResource(ResourceProvider::Bitmap, resource_provider.get(), NULL);
InitializeGLAndCheck(shared_data.get(),
resource_provider.get(),
output_surface.get());
resource_provider->InitializeSoftware();
CheckCreateResource(ResourceProvider::Bitmap, resource_provider.get(), NULL);
InitializeGLAndCheck(shared_data.get(),
resource_provider.get(),
output_surface.get());
}
INSTANTIATE_TEST_CASE_P(
ResourceProviderTests,
ResourceProviderTest,
::testing::Values(ResourceProvider::GLTexture, ResourceProvider::Bitmap));
} // namespace
} // namespace cc