blob: e948d23c0032e6f6a1c6d78576aaf6e2cdab4436 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/client/client_resource_provider.h"
#include <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/test/gtest_util.h"
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/common/resources/single_release_callback.h"
#include "components/viz/test/test_context_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::Each;
namespace viz {
namespace {
class ClientResourceProviderTest : public testing::TestWithParam<bool> {
protected:
ClientResourceProviderTest()
: use_gpu_(GetParam()),
context_provider_(TestContextProvider::Create()),
bound_(context_provider_->BindToCurrentThread()) {
DCHECK_EQ(bound_, gpu::ContextResult::kSuccess);
}
void SetUp() override {
provider_ = std::make_unique<ClientResourceProvider>(
/*delegated_sync_points_required=*/true);
}
void TearDown() override { provider_ = nullptr; }
gpu::Mailbox MailboxFromChar(char value) {
gpu::Mailbox mailbox;
memset(mailbox.name, value, sizeof(mailbox.name));
return mailbox;
}
gpu::SyncToken SyncTokenFromUInt(uint32_t value) {
return gpu::SyncToken(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x123), value);
}
TransferableResource MakeTransferableResource(bool gpu,
char mailbox_char,
uint32_t sync_token_value) {
TransferableResource r;
r.id = mailbox_char;
r.is_software = !gpu;
r.filter = 456;
r.size = gfx::Size(10, 11);
r.mailbox_holder.mailbox = MailboxFromChar(mailbox_char);
if (gpu) {
r.mailbox_holder.sync_token = SyncTokenFromUInt(sync_token_value);
r.mailbox_holder.texture_target = 6;
}
return r;
}
bool use_gpu() const { return use_gpu_; }
ClientResourceProvider& provider() const { return *provider_; }
ContextProvider* context_provider() const { return context_provider_.get(); }
void DestroyProvider() {
provider_->ShutdownAndReleaseAllResources();
provider_ = nullptr;
}
private:
bool use_gpu_;
scoped_refptr<TestContextProvider> context_provider_;
gpu::ContextResult bound_;
std::unique_ptr<ClientResourceProvider> provider_;
};
INSTANTIATE_TEST_SUITE_P(ClientResourceProviderTests,
ClientResourceProviderTest,
::testing::Values(false, true));
class MockReleaseCallback {
public:
MOCK_METHOD2(Released, void(const gpu::SyncToken& token, bool lost));
MOCK_METHOD3(ReleasedWithId,
void(ResourceId id, const gpu::SyncToken& token, bool lost));
};
TEST_P(ClientResourceProviderTest, TransferableResourceReleased) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId id = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// The local id is different.
EXPECT_NE(id, tran.id);
// The same SyncToken that was sent is returned when the resource was never
// exported. The SyncToken may be from any context, and the ReleaseCallback
// may need to wait on it before interacting with the resource on its context.
EXPECT_CALL(release, Released(tran.mailbox_holder.sync_token, false));
provider().RemoveImportedResource(id);
}
TEST_P(ClientResourceProviderTest, TransferableResourceSendToParent) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId id = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// Export the resource.
std::vector<ResourceId> to_send = {id};
std::vector<TransferableResource> exported;
provider().PrepareSendToParent(to_send, &exported, context_provider());
ASSERT_EQ(exported.size(), 1u);
// Exported resource matches except for the id which was mapped
// to the local ResourceProvider, and the sync token should be
// verified if it's a gpu resource.
gpu::SyncToken verified_sync_token = tran.mailbox_holder.sync_token;
if (!tran.is_software)
verified_sync_token.SetVerifyFlush();
EXPECT_EQ(exported[0].id, id);
EXPECT_EQ(exported[0].is_software, tran.is_software);
EXPECT_EQ(exported[0].filter, tran.filter);
EXPECT_EQ(exported[0].size, tran.size);
EXPECT_EQ(exported[0].mailbox_holder.mailbox, tran.mailbox_holder.mailbox);
EXPECT_EQ(exported[0].mailbox_holder.sync_token, verified_sync_token);
EXPECT_EQ(exported[0].mailbox_holder.texture_target,
tran.mailbox_holder.texture_target);
// Exported resources are not released when removed, until the export returns.
EXPECT_CALL(release, Released(_, _)).Times(0);
provider().RemoveImportedResource(id);
// Return the resource, with a sync token if using gpu.
std::vector<ReturnedResource> returned;
returned.push_back({});
returned.back().id = exported[0].id;
if (use_gpu())
returned.back().sync_token = SyncTokenFromUInt(31);
returned.back().count = 1;
returned.back().lost = false;
// The sync token is given to the ReleaseCallback.
EXPECT_CALL(release, Released(returned[0].sync_token, false));
provider().ReceiveReturnsFromParent(returned);
}
TEST_P(ClientResourceProviderTest, TransferableResourceSendTwoToParent) {
TransferableResource tran[] = {MakeTransferableResource(use_gpu(), 'a', 15),
MakeTransferableResource(use_gpu(), 'b', 16)};
ResourceId id1 = provider().ImportResource(
tran[0], SingleReleaseCallback::Create(base::DoNothing()));
ResourceId id2 = provider().ImportResource(
tran[1], SingleReleaseCallback::Create(base::DoNothing()));
// Export the resource.
std::vector<ResourceId> to_send = {id1, id2};
std::vector<TransferableResource> exported;
provider().PrepareSendToParent(to_send, &exported, context_provider());
ASSERT_EQ(exported.size(), 2u);
// Exported resource matches except for the id which was mapped
// to the local ResourceProvider, and the sync token should be
// verified if it's a gpu resource.
for (int i = 0; i < 2; ++i) {
gpu::SyncToken verified_sync_token = tran[i].mailbox_holder.sync_token;
if (!tran[i].is_software)
verified_sync_token.SetVerifyFlush();
EXPECT_EQ(exported[i].id, to_send[i]);
EXPECT_EQ(exported[i].is_software, tran[i].is_software);
EXPECT_EQ(exported[i].filter, tran[i].filter);
EXPECT_EQ(exported[i].size, tran[i].size);
EXPECT_EQ(exported[i].mailbox_holder.mailbox,
tran[i].mailbox_holder.mailbox);
EXPECT_EQ(exported[i].mailbox_holder.sync_token, verified_sync_token);
EXPECT_EQ(exported[i].mailbox_holder.texture_target,
tran[i].mailbox_holder.texture_target);
}
provider().RemoveImportedResource(id1);
provider().RemoveImportedResource(id2);
DestroyProvider();
}
TEST_P(ClientResourceProviderTest, TransferableResourceSendToParentTwoTimes) {
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId id = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::DoNothing()));
// Export the resource.
std::vector<ResourceId> to_send = {id};
std::vector<TransferableResource> exported;
provider().PrepareSendToParent(to_send, &exported, context_provider());
ASSERT_EQ(exported.size(), 1u);
EXPECT_EQ(exported[0].id, id);
// Return the resource, with a sync token if using gpu.
std::vector<ReturnedResource> returned;
returned.push_back({});
returned.back().id = exported[0].id;
if (use_gpu())
returned.back().sync_token = SyncTokenFromUInt(31);
returned.back().count = 1;
returned.back().lost = false;
provider().ReceiveReturnsFromParent(returned);
// Then export again, it still sends.
exported.clear();
provider().PrepareSendToParent(to_send, &exported, context_provider());
ASSERT_EQ(exported.size(), 1u);
EXPECT_EQ(exported[0].id, id);
provider().RemoveImportedResource(id);
DestroyProvider();
}
TEST_P(ClientResourceProviderTest,
TransferableResourceLostOnShutdownIfExported) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId id = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// Export the resource.
std::vector<ResourceId> to_send = {id};
std::vector<TransferableResource> exported;
provider().PrepareSendToParent(to_send, &exported, context_provider());
provider().RemoveImportedResource(id);
EXPECT_CALL(release, Released(_, true));
DestroyProvider();
}
TEST_P(ClientResourceProviderTest, TransferableResourceSendToParentManyUnsent) {
MockReleaseCallback release;
// 5 resources to have 2 before and 2 after the one being sent and returned,
// to verify the data structures are treated correctly when removing from the
// middle. 1 after is not enough as that one will replace the resource being
// removed and misses some edge cases. We want another resource after that in
// the structure to verify it is also not treated incorrectly.
struct Data {
TransferableResource tran;
ResourceId id;
} data[5];
for (int i = 0; i < 5; ++i) {
data[i].tran = MakeTransferableResource(use_gpu(), 'a', 15);
data[i].id = provider().ImportResource(
data[i].tran,
SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
}
std::sort(std::begin(data), std::end(data),
[](const Data& a, const Data& b) { return a.id < b.id; });
// Export the resource.
std::vector<ResourceId> to_send = {data[2].id};
std::vector<TransferableResource> exported;
provider().PrepareSendToParent(to_send, &exported, context_provider());
ASSERT_EQ(exported.size(), 1u);
// Exported resource matches except for the id which was mapped
// to the local ResourceProvider, and the sync token should be
// verified if it's a gpu resource.
gpu::SyncToken verified_sync_token = data[2].tran.mailbox_holder.sync_token;
if (!data[2].tran.is_software)
verified_sync_token.SetVerifyFlush();
// Exported resources are not released when removed, until the export returns.
EXPECT_CALL(release, Released(_, _)).Times(0);
provider().RemoveImportedResource(data[2].id);
// Return the resource, with a sync token if using gpu.
std::vector<ReturnedResource> returned;
returned.push_back({});
returned.back().id = exported[0].id;
if (use_gpu())
returned.back().sync_token = SyncTokenFromUInt(31);
returned.back().count = 1;
returned.back().lost = false;
// The sync token is given to the ReleaseCallback.
EXPECT_CALL(release, Released(returned[0].sync_token, false));
provider().ReceiveReturnsFromParent(returned);
EXPECT_CALL(release, Released(_, false)).Times(4);
provider().RemoveImportedResource(data[0].id);
provider().RemoveImportedResource(data[1].id);
provider().RemoveImportedResource(data[3].id);
provider().RemoveImportedResource(data[4].id);
}
TEST_P(ClientResourceProviderTest, TransferableResourceRemovedAfterReturn) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId id = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// Export the resource.
std::vector<ResourceId> to_send = {id};
std::vector<TransferableResource> exported;
provider().PrepareSendToParent(to_send, &exported, context_provider());
// Return the resource. This does not release the resource back to
// the client.
std::vector<ReturnedResource> returned;
returned.push_back({});
returned.back().id = exported[0].id;
if (use_gpu())
returned.back().sync_token = SyncTokenFromUInt(31);
returned.back().count = 1;
returned.back().lost = false;
EXPECT_CALL(release, Released(_, _)).Times(0);
provider().ReceiveReturnsFromParent(returned);
// Once removed, the resource is released.
EXPECT_CALL(release, Released(returned[0].sync_token, false));
provider().RemoveImportedResource(id);
}
TEST_P(ClientResourceProviderTest, TransferableResourceExportedTwice) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId id = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// Export the resource once.
std::vector<ResourceId> to_send = {id};
std::vector<TransferableResource> exported;
provider().PrepareSendToParent(to_send, &exported, context_provider());
// Exported resources are not released when removed, until all exports are
// returned.
EXPECT_CALL(release, Released(_, _)).Times(0);
provider().RemoveImportedResource(id);
// Export the resource twice.
exported = {};
provider().PrepareSendToParent(to_send, &exported, context_provider());
// Return the resource the first time.
std::vector<ReturnedResource> returned;
returned.push_back({});
returned.back().id = exported[0].id;
if (use_gpu())
returned.back().sync_token = SyncTokenFromUInt(31);
returned.back().count = 1;
returned.back().lost = false;
provider().ReceiveReturnsFromParent(returned);
// And a second time, with a different sync token. Now the ReleaseCallback can
// happen, using the latest sync token.
if (use_gpu())
returned.back().sync_token = SyncTokenFromUInt(47);
EXPECT_CALL(release, Released(returned[0].sync_token, false));
provider().ReceiveReturnsFromParent(returned);
}
TEST_P(ClientResourceProviderTest, TransferableResourceReturnedTwiceAtOnce) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId id = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// Export the resource once.
std::vector<ResourceId> to_send = {id};
std::vector<TransferableResource> exported;
provider().PrepareSendToParent(to_send, &exported, context_provider());
// Exported resources are not released when removed, until all exports are
// returned.
EXPECT_CALL(release, Released(_, _)).Times(0);
provider().RemoveImportedResource(id);
// Export the resource twice.
exported = {};
provider().PrepareSendToParent(to_send, &exported, context_provider());
// Return both exports at once.
std::vector<ReturnedResource> returned;
returned.push_back({});
returned.back().id = exported[0].id;
if (use_gpu())
returned.back().sync_token = SyncTokenFromUInt(31);
returned.back().count = 2;
returned.back().lost = false;
// When returned, the ReleaseCallback can happen, using the latest sync token.
EXPECT_CALL(release, Released(returned[0].sync_token, false));
provider().ReceiveReturnsFromParent(returned);
}
TEST_P(ClientResourceProviderTest, TransferableResourceLostOnReturn) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId id = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// Export the resource once.
std::vector<ResourceId> to_send = {id};
std::vector<TransferableResource> exported;
provider().PrepareSendToParent(to_send, &exported, context_provider());
// Exported resources are not released when removed, until all exports are
// returned.
EXPECT_CALL(release, Released(_, _)).Times(0);
provider().RemoveImportedResource(id);
// Export the resource twice.
exported = {};
provider().PrepareSendToParent(to_send, &exported, context_provider());
// Return the resource the first time, not lost.
std::vector<ReturnedResource> returned;
returned.push_back({});
returned.back().id = exported[0].id;
returned.back().count = 1;
returned.back().lost = false;
provider().ReceiveReturnsFromParent(returned);
// Return a second time, as lost. The ReturnCallback should report it
// lost.
returned.back().lost = true;
EXPECT_CALL(release, Released(_, true));
provider().ReceiveReturnsFromParent(returned);
}
TEST_P(ClientResourceProviderTest, TransferableResourceLostOnFirstReturn) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId id = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// Export the resource once.
std::vector<ResourceId> to_send = {id};
std::vector<TransferableResource> exported;
provider().PrepareSendToParent(to_send, &exported, context_provider());
// Exported resources are not released when removed, until all exports are
// returned.
EXPECT_CALL(release, Released(_, _)).Times(0);
provider().RemoveImportedResource(id);
// Export the resource twice.
exported = {};
provider().PrepareSendToParent(to_send, &exported, context_provider());
// Return the resource the first time, marked as lost.
std::vector<ReturnedResource> returned;
returned.push_back({});
returned.back().id = exported[0].id;
returned.back().count = 1;
returned.back().lost = true;
provider().ReceiveReturnsFromParent(returned);
// Return a second time, not lost. The first lost signal should not be lost.
returned.back().lost = false;
EXPECT_CALL(release, Released(_, true));
provider().ReceiveReturnsFromParent(returned);
}
TEST_P(ClientResourceProviderTest, ReturnedSyncTokensArePassedToClient) {
// SyncTokens are gpu-only.
if (!use_gpu())
return;
MockReleaseCallback release;
GLuint texture;
context_provider()->ContextGL()->GenTextures(1, &texture);
context_provider()->ContextGL()->BindTexture(GL_TEXTURE_2D, texture);
gpu::Mailbox mailbox;
context_provider()->ContextGL()->ProduceTextureDirectCHROMIUM(texture,
mailbox.name);
gpu::SyncToken sync_token;
context_provider()->ContextGL()->GenSyncTokenCHROMIUM(sync_token.GetData());
auto tran = TransferableResource::MakeGL(mailbox, GL_LINEAR, GL_TEXTURE_2D,
sync_token);
ResourceId resource = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
EXPECT_TRUE(tran.mailbox_holder.sync_token.HasData());
// All the logic below assumes that the sync token releases are all positive.
EXPECT_LT(0u, tran.mailbox_holder.sync_token.release_count());
// Transfer the resource, expect the sync points to be consistent.
std::vector<TransferableResource> list;
provider().PrepareSendToParent({resource}, &list, context_provider());
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)));
// Make a new texture id from the mailbox.
context_provider()->ContextGL()->WaitSyncTokenCHROMIUM(
list[0].mailbox_holder.sync_token.GetConstData());
unsigned other_texture =
context_provider()->ContextGL()->CreateAndConsumeTextureCHROMIUM(
mailbox.name);
// Then delete it and make a new SyncToken.
context_provider()->ContextGL()->DeleteTextures(1, &other_texture);
context_provider()->ContextGL()->GenSyncTokenCHROMIUM(
list[0].mailbox_holder.sync_token.GetData());
EXPECT_TRUE(list[0].mailbox_holder.sync_token.HasData());
// Receive the resource, then delete it, expect the SyncTokens to be
// consistent.
provider().ReceiveReturnsFromParent(
TransferableResource::ReturnResources(list));
gpu::SyncToken returned_sync_token;
EXPECT_CALL(release, Released(_, false))
.WillOnce(testing::SaveArg<0>(&returned_sync_token));
provider().RemoveImportedResource(resource);
EXPECT_GE(returned_sync_token.release_count(),
list[0].mailbox_holder.sync_token.release_count());
}
TEST_P(ClientResourceProviderTest, LostResourcesAreReturnedLost) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId resource = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// Transfer the resource to the parent.
std::vector<TransferableResource> list;
provider().PrepareSendToParent({resource}, &list, context_provider());
EXPECT_EQ(1u, list.size());
// Receive it back marked lost.
std::vector<ReturnedResource> returned_to_child;
returned_to_child.push_back(list[0].ToReturnedResource());
returned_to_child.back().lost = true;
provider().ReceiveReturnsFromParent(returned_to_child);
// Delete the resource in the child. Expect the resource to be lost.
EXPECT_CALL(release, Released(_, true));
provider().RemoveImportedResource(resource);
}
TEST_P(ClientResourceProviderTest, ShutdownLosesExportedResources) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId resource = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// Transfer the resource to the parent.
std::vector<TransferableResource> list;
provider().PrepareSendToParent({resource}, &list, context_provider());
EXPECT_EQ(1u, list.size());
// Remove it in the ClientResourceProvider, but since it's exported it's not
// returned yet.
provider().RemoveImportedResource(resource);
// Destroy the ClientResourceProvider, the resource is returned lost.
EXPECT_CALL(release, Released(_, true));
DestroyProvider();
}
TEST_P(ClientResourceProviderTest, ReleaseExportedResources) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId resource = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// Transfer the resource to the parent.
std::vector<TransferableResource> list;
provider().PrepareSendToParent({resource}, &list, context_provider());
EXPECT_EQ(1u, list.size());
// Remove it in the ClientResourceProvider, but since it's exported it's not
// returned yet.
provider().RemoveImportedResource(resource);
// Drop any exported resources. They are returned lost for gpu compositing,
// since gpu resources are modified (in their metadata) while being used by
// the parent.
EXPECT_CALL(release, Released(_, use_gpu()));
provider().ReleaseAllExportedResources(use_gpu());
EXPECT_CALL(release, Released(_, _)).Times(0);
}
TEST_P(ClientResourceProviderTest, ReleaseExportedResourcesThenRemove) {
MockReleaseCallback release;
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 15);
ResourceId resource = provider().ImportResource(
tran, SingleReleaseCallback::Create(base::BindOnce(
&MockReleaseCallback::Released, base::Unretained(&release))));
// Transfer the resource to the parent.
std::vector<TransferableResource> list;
provider().PrepareSendToParent({resource}, &list, context_provider());
EXPECT_EQ(1u, list.size());
// Drop any exported resources. They are now considered lost for gpu
// compositing, since gpu resources are modified (in their metadata) while
// being used by the parent.
provider().ReleaseAllExportedResources(use_gpu());
EXPECT_CALL(release, Released(_, use_gpu()));
// Remove it in the ClientResourceProvider, it was exported so wouldn't be
// released here, except that we dropped the export above.
provider().RemoveImportedResource(resource);
EXPECT_CALL(release, Released(_, _)).Times(0);
}
TEST_P(ClientResourceProviderTest, ReleaseMultipleResources) {
MockReleaseCallback release;
// Make 5 resources, put them in a non-sorted order.
ResourceId resources[5];
for (int i = 0; i < 5; ++i) {
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 1 + i);
resources[i] = provider().ImportResource(
tran, SingleReleaseCallback::Create(
base::BindOnce(&MockReleaseCallback::ReleasedWithId,
base::Unretained(&release), i)));
}
// Transfer some resources to the parent, but not in the sorted order.
std::vector<TransferableResource> list;
provider().PrepareSendToParent({resources[2], resources[0], resources[4]},
&list, context_provider());
EXPECT_EQ(3u, list.size());
// Receive them back. Since these are not in the same order they were
// inserted originally, we verify the ClientResourceProvider can handle
// resources being returned (in a group) that are not at the front/back
// of its internal storage. IOW This shouldn't crash or corrupt state.
std::vector<ReturnedResource> returned_to_child;
returned_to_child.push_back(list[0].ToReturnedResource());
returned_to_child.push_back(list[1].ToReturnedResource());
returned_to_child.push_back(list[2].ToReturnedResource());
provider().ReceiveReturnsFromParent(returned_to_child);
// Remove them from the ClientResourceProvider, they should be returned as
// they're no longer exported.
EXPECT_CALL(release, ReleasedWithId(0, _, false));
provider().RemoveImportedResource(resources[0]);
EXPECT_CALL(release, ReleasedWithId(2, _, false));
provider().RemoveImportedResource(resources[2]);
EXPECT_CALL(release, ReleasedWithId(4, _, false));
provider().RemoveImportedResource(resources[4]);
// These were never exported.
EXPECT_CALL(release, ReleasedWithId(1, _, false));
EXPECT_CALL(release, ReleasedWithId(3, _, false));
provider().RemoveImportedResource(resources[1]);
provider().RemoveImportedResource(resources[3]);
EXPECT_CALL(release, Released(_, false)).Times(0);
}
TEST_P(ClientResourceProviderTest, ReleaseMultipleResourcesBeforeReturn) {
MockReleaseCallback release;
// Make 5 resources, put them in a non-sorted order.
ResourceId resources[5];
for (int i = 0; i < 5; ++i) {
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 1 + i);
resources[i] = provider().ImportResource(
tran, SingleReleaseCallback::Create(
base::BindOnce(&MockReleaseCallback::ReleasedWithId,
base::Unretained(&release), i)));
}
// Transfer some resources to the parent, but not in the sorted order.
std::vector<TransferableResource> list;
provider().PrepareSendToParent({resources[2], resources[0], resources[4]},
&list, context_provider());
EXPECT_EQ(3u, list.size());
// Remove the exported resources from the ClientResourceProvider, they should
// not yet be returned, since they are exported.
provider().RemoveImportedResource(resources[0]);
provider().RemoveImportedResource(resources[2]);
provider().RemoveImportedResource(resources[4]);
// Receive them back now. Since these are not in the same order they were
// inserted originally, we verify the ClientResourceProvider can handle
// resources being returned (in a group) that are not at the front/back
// of its internal storage. IOW This shouldn't crash or corrupt state.
std::vector<ReturnedResource> returned_to_child;
returned_to_child.push_back(list[0].ToReturnedResource());
returned_to_child.push_back(list[1].ToReturnedResource());
returned_to_child.push_back(list[2].ToReturnedResource());
// The resources are returned immediately since they were previously removed.
EXPECT_CALL(release, ReleasedWithId(0, _, false));
EXPECT_CALL(release, ReleasedWithId(2, _, false));
EXPECT_CALL(release, ReleasedWithId(4, _, false));
provider().ReceiveReturnsFromParent(returned_to_child);
// These were never exported.
EXPECT_CALL(release, ReleasedWithId(1, _, false));
EXPECT_CALL(release, ReleasedWithId(3, _, false));
provider().RemoveImportedResource(resources[1]);
provider().RemoveImportedResource(resources[3]);
EXPECT_CALL(release, Released(_, false)).Times(0);
}
TEST_P(ClientResourceProviderTest, ReturnDuplicateResourceBeforeRemove) {
MockReleaseCallback release;
// Make 5 resources, put them in a non-sorted order.
ResourceId resources[5];
for (int i = 0; i < 5; ++i) {
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 1 + i);
resources[i] = provider().ImportResource(
tran, SingleReleaseCallback::Create(
base::BindOnce(&MockReleaseCallback::ReleasedWithId,
base::Unretained(&release), i)));
}
// Transfer a resource to the parent, do it twice.
std::vector<TransferableResource> list;
provider().PrepareSendToParent({resources[2]}, &list, context_provider());
list.clear();
provider().PrepareSendToParent({resources[2]}, &list, context_provider());
// Receive the resource back. It's possible that the parent may return
// the same ResourceId multiple times in the same message, which we test
// for here.
std::vector<ReturnedResource> returned_to_child;
returned_to_child.push_back(list[0].ToReturnedResource());
returned_to_child.push_back(list[0].ToReturnedResource());
provider().ReceiveReturnsFromParent(returned_to_child);
// Remove it from the ClientResourceProvider, it should be returned as
// it's no longer exported.
EXPECT_CALL(release, ReleasedWithId(2, _, false));
provider().RemoveImportedResource(resources[2]);
// These were never exported.
EXPECT_CALL(release, ReleasedWithId(0, _, false));
EXPECT_CALL(release, ReleasedWithId(1, _, false));
EXPECT_CALL(release, ReleasedWithId(3, _, false));
EXPECT_CALL(release, ReleasedWithId(4, _, false));
provider().RemoveImportedResource(resources[0]);
provider().RemoveImportedResource(resources[1]);
provider().RemoveImportedResource(resources[3]);
provider().RemoveImportedResource(resources[4]);
EXPECT_CALL(release, Released(_, false)).Times(0);
}
TEST_P(ClientResourceProviderTest, ReturnDuplicateResourceAfterRemove) {
MockReleaseCallback release;
// Make 5 resources, put them in a non-sorted order.
ResourceId resources[5];
for (int i = 0; i < 5; ++i) {
TransferableResource tran = MakeTransferableResource(use_gpu(), 'a', 1 + i);
resources[i] = provider().ImportResource(
tran, SingleReleaseCallback::Create(
base::BindOnce(&MockReleaseCallback::ReleasedWithId,
base::Unretained(&release), i)));
}
// Transfer a resource to the parent, do it twice.
std::vector<TransferableResource> list;
provider().PrepareSendToParent({resources[2]}, &list, context_provider());
list.clear();
provider().PrepareSendToParent({resources[2]}, &list, context_provider());
// Remove it from the ClientResourceProvider, it should not be returned yet
// as it's still exported.
provider().RemoveImportedResource(resources[2]);
// Receive the resource back. It's possible that the parent may return
// the same ResourceId multiple times in the same message, which we test
// for here.
std::vector<ReturnedResource> returned_to_child;
returned_to_child.push_back(list[0].ToReturnedResource());
returned_to_child.push_back(list[0].ToReturnedResource());
// Once no longer exported, since it was removed earlier, it will be returned
// immediately.
EXPECT_CALL(release, ReleasedWithId(2, _, false));
provider().ReceiveReturnsFromParent(returned_to_child);
// These were never exported.
EXPECT_CALL(release, ReleasedWithId(0, _, false));
EXPECT_CALL(release, ReleasedWithId(1, _, false));
EXPECT_CALL(release, ReleasedWithId(3, _, false));
EXPECT_CALL(release, ReleasedWithId(4, _, false));
provider().RemoveImportedResource(resources[0]);
provider().RemoveImportedResource(resources[1]);
provider().RemoveImportedResource(resources[3]);
provider().RemoveImportedResource(resources[4]);
EXPECT_CALL(release, Released(_, false)).Times(0);
}
} // namespace
} // namespace viz