blob: c2694679a48701b6e5f7eaa65ff98cad7d8a3c6b [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/service/display/display_resource_provider_gl.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <set>
#include <unordered_map>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/check.h"
#include "base/memory/ref_counted.h"
#include "build/build_config.h"
#include "components/viz/client/client_resource_provider.h"
#include "components/viz/common/resources/release_callback.h"
#include "components/viz/common/resources/resource_format_utils.h"
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/test/test_context_provider.h"
#include "components/viz/test/test_gles2_interface.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"
using testing::_;
using testing::ByMove;
using testing::DoAll;
using testing::Return;
using testing::SaveArg;
namespace viz {
namespace {
class MockReleaseCallback {
public:
MOCK_METHOD2(Released, void(const gpu::SyncToken& token, bool lost));
};
MATCHER_P(MatchesSyncToken, sync_token, "") {
gpu::SyncToken other;
memcpy(&other, arg, sizeof(other));
return other == sync_token;
}
static void CollectResources(std::vector<ReturnedResource>* array,
std::vector<ReturnedResource> returned) {
array->insert(array->end(), std::make_move_iterator(returned.begin()),
std::make_move_iterator(returned.end()));
}
class ResourceProviderGLES2Interface : public TestGLES2Interface {
public:
ResourceProviderGLES2Interface() = default;
void WaitSyncTokenCHROMIUM(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_;
}
private:
gpu::SyncToken last_waited_sync_token_;
};
class DisplayResourceProviderGLTest : public testing::Test {
public:
DisplayResourceProviderGLTest() {
auto gl_owned = std::make_unique<ResourceProviderGLES2Interface>();
gl_ = gl_owned.get();
context_provider_ = TestContextProvider::Create(std::move(gl_owned));
context_provider_->UnboundTestContextGL()
->set_support_texture_format_bgra8888(true);
context_provider_->BindToCurrentThread();
child_context_provider_ = TestContextProvider::Create();
child_context_provider_->UnboundTestContextGL()
->set_support_texture_format_bgra8888(true);
child_context_provider_->BindToCurrentThread();
resource_provider_ =
std::make_unique<DisplayResourceProviderGL>(context_provider_.get());
child_resource_provider_ = std::make_unique<ClientResourceProvider>();
}
~DisplayResourceProviderGLTest() override {
child_resource_provider_->ShutdownAndReleaseAllResources();
}
static ReturnCallback GetReturnCallback(
std::vector<ReturnedResource>* array) {
return base::BindRepeating(&CollectResources, array);
}
static void SetResourceFilter(DisplayResourceProviderGL* resource_provider,
ResourceId id,
GLenum filter) {
DisplayResourceProviderGL::ScopedSamplerGL sampler(resource_provider, id,
GL_TEXTURE_2D, filter);
}
TransferableResource CreateResource(ResourceFormat format) {
constexpr gfx::Size size(64, 64);
gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate();
gpu::SyncToken sync_token = GenSyncToken();
EXPECT_TRUE(sync_token.HasData());
TransferableResource gl_resource = TransferableResource::MakeGL(
gpu_mailbox, GL_LINEAR, GL_TEXTURE_2D, sync_token, size,
false /* is_overlay_candidate */);
gl_resource.format = format;
return gl_resource;
}
ResourceId MakeGpuResourceAndSendToDisplay(
GLuint filter,
GLuint target,
const gpu::SyncToken& sync_token,
DisplayResourceProvider* resource_provider) {
ReturnCallback return_callback = base::DoNothing();
int child = resource_provider->CreateChild(return_callback, SurfaceId());
gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate();
constexpr gfx::Size size(64, 64);
auto resource =
TransferableResource::MakeGL(gpu_mailbox, GL_LINEAR, target, sync_token,
size, false /* is_overlay_candidate */);
resource.id = ResourceId(11);
resource_provider->ReceiveFromChild(child, {resource});
auto& map = resource_provider->GetChildToParentMap(child);
return map.find(resource.id)->second;
}
gpu::SyncToken GenSyncToken() {
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x123),
next_fence_sync_++);
sync_token.SetVerifyFlush();
return sync_token;
}
protected:
ResourceProviderGLES2Interface* gl_ = nullptr;
uint64_t next_fence_sync_ = 1;
scoped_refptr<TestContextProvider> context_provider_;
scoped_refptr<TestContextProvider> child_context_provider_;
std::unique_ptr<DisplayResourceProviderGL> resource_provider_;
std::unique_ptr<ClientResourceProvider> child_resource_provider_;
};
TEST_F(DisplayResourceProviderGLTest, ReadLockCountStopsReturnToChildOrDelete) {
MockReleaseCallback release;
TransferableResource tran = CreateResource(RGBA_8888);
ResourceId id1 = child_resource_provider_->ImportResource(
tran, base::BindOnce(&MockReleaseCallback::Released,
base::Unretained(&release)));
std::vector<ReturnedResource> returned_to_child;
int child_id = resource_provider_->CreateChild(
GetReturnCallback(&returned_to_child), SurfaceId());
{
// Transfer some resources to the parent.
std::vector<TransferableResource> list;
child_resource_provider_->PrepareSendToParent(
{id1}, &list,
static_cast<RasterContextProvider*>(child_context_provider_.get()));
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.
std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
resource_provider_->GetChildToParentMap(child_id);
ResourceId mapped_resource_id = resource_map[list[0].id];
resource_provider_->WaitSyncToken(mapped_resource_id);
DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(),
mapped_resource_id);
resource_provider_->DeclareUsedResourcesFromChild(child_id,
ResourceIdSet());
EXPECT_EQ(0u, returned_to_child.size());
}
EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider_->ReceiveReturnsFromParent(
std::move(returned_to_child));
// No need to wait for the sync token here -- it will be returned to the
// client on delete.
{
EXPECT_CALL(release, Released(_, _));
child_resource_provider_->RemoveImportedResource(id1);
}
resource_provider_->DestroyChild(child_id);
}
class TestFence : public ResourceFence {
public:
TestFence() = default;
// ResourceFence implementation.
void Set() override {}
bool HasPassed() override { return passed; }
bool passed = false;
private:
~TestFence() override = default;
};
TEST_F(DisplayResourceProviderGLTest, ReadLockFenceStopsReturnToChildOrDelete) {
MockReleaseCallback release;
TransferableResource tran1 = CreateResource(RGBA_8888);
tran1.read_lock_fences_enabled = true;
ResourceId id1 = child_resource_provider_->ImportResource(
tran1, base::BindOnce(&MockReleaseCallback::Released,
base::Unretained(&release)));
std::vector<ReturnedResource> returned_to_child;
int child_id = resource_provider_->CreateChild(
GetReturnCallback(&returned_to_child), SurfaceId());
// Transfer some resources to the parent.
std::vector<TransferableResource> list;
child_resource_provider_->PrepareSendToParent(
{id1}, &list,
static_cast<RasterContextProvider*>(child_context_provider_.get()));
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.
std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
resource_provider_->GetChildToParentMap(child_id);
scoped_refptr<TestFence> fence(new TestFence);
resource_provider_->SetReadLockFence(fence.get());
{
ResourceId parent_id = resource_map[list.front().id];
resource_provider_->WaitSyncToken(parent_id);
DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider_.get(),
parent_id);
}
resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
EXPECT_EQ(0u, returned_to_child.size());
resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
EXPECT_EQ(0u, returned_to_child.size());
fence->passed = true;
resource_provider_->DeclareUsedResourcesFromChild(child_id, ResourceIdSet());
EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider_->ReceiveReturnsFromParent(
std::move(returned_to_child));
EXPECT_CALL(release, Released(_, _));
child_resource_provider_->RemoveImportedResource(id1);
}
TEST_F(DisplayResourceProviderGLTest, ReadLockFenceDestroyChild) {
MockReleaseCallback release;
TransferableResource tran1 = CreateResource(RGBA_8888);
tran1.read_lock_fences_enabled = true;
ResourceId id1 = child_resource_provider_->ImportResource(
tran1, base::BindOnce(&MockReleaseCallback::Released,
base::Unretained(&release)));
TransferableResource tran2 = CreateResource(RGBA_8888);
tran2.read_lock_fences_enabled = false;
ResourceId id2 = child_resource_provider_->ImportResource(
tran2, base::BindOnce(&MockReleaseCallback::Released,
base::Unretained(&release)));
std::vector<ReturnedResource> returned_to_child;
int child_id = resource_provider_->CreateChild(
GetReturnCallback(&returned_to_child), SurfaceId());
// Transfer resources to the parent.
std::vector<TransferableResource> list;
child_resource_provider_->PrepareSendToParent(
{id1, id2}, &list,
static_cast<RasterContextProvider*>(child_context_provider_.get()));
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.
std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
resource_provider_->GetChildToParentMap(child_id);
scoped_refptr<TestFence> fence(new TestFence);
resource_provider_->SetReadLockFence(fence.get());
{
for (auto& resource : list) {
ResourceId parent_id = resource_map[resource.id];
resource_provider_->WaitSyncToken(parent_id);
DisplayResourceProviderGL::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(
std::move(returned_to_child));
EXPECT_CALL(release, Released(_, _)).Times(2);
child_resource_provider_->RemoveImportedResource(id1);
child_resource_provider_->RemoveImportedResource(id2);
}
// Test that ScopedBatchReturnResources batching works.
TEST_F(DisplayResourceProviderGLTest,
ScopedBatchReturnResourcesPreventsReturn) {
MockReleaseCallback release;
std::vector<ReturnedResource> returned_to_child;
int child_id = resource_provider_->CreateChild(
GetReturnCallback(&returned_to_child), SurfaceId());
// Transfer some resources to the parent.
constexpr size_t kTotalResources = 5;
constexpr size_t kLockedResources = 3;
constexpr size_t kUsedResources = 4;
ResourceId ids[kTotalResources];
for (auto& id : ids) {
TransferableResource tran = CreateResource(RGBA_8888);
id = child_resource_provider_->ImportResource(
tran, base::BindOnce(&MockReleaseCallback::Released,
base::Unretained(&release)));
}
std::vector<ResourceId> resource_ids_to_transfer(ids, ids + kTotalResources);
std::vector<TransferableResource> list;
child_resource_provider_->PrepareSendToParent(
resource_ids_to_transfer, &list,
static_cast<RasterContextProvider*>(child_context_provider_.get()));
ASSERT_EQ(kTotalResources, list.size());
for (const auto& id : ids)
EXPECT_TRUE(child_resource_provider_->InUseByConsumer(id));
resource_provider_->ReceiveFromChild(child_id, list);
// In DisplayResourceProvider's namespace, use the mapped resource id.
std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
resource_provider_->GetChildToParentMap(child_id);
std::vector<std::unique_ptr<DisplayResourceProviderGL::ScopedReadLockGL>>
read_locks;
for (size_t i = 0; i < kLockedResources; i++) {
ResourceId mapped_resource_id = resource_map[ids[i]];
resource_provider_->WaitSyncToken(mapped_resource_id);
read_locks.push_back(
std::make_unique<DisplayResourceProviderGL::ScopedReadLockGL>(
resource_provider_.get(), mapped_resource_id));
}
// Mark all locked resources, and one unlocked resource as used for first
// batch.
{
DisplayResourceProviderGL::ScopedBatchReturnResources returner(
resource_provider_.get());
resource_provider_->DeclareUsedResourcesFromChild(
child_id, ResourceIdSet(ids, ids + kUsedResources));
EXPECT_EQ(0u, returned_to_child.size());
}
EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider_->ReceiveReturnsFromParent(
std::move(returned_to_child));
returned_to_child.clear();
// Return all locked resources.
{
DisplayResourceProviderGL::ScopedBatchReturnResources returner(
resource_provider_.get());
resource_provider_->DeclareUsedResourcesFromChild(
child_id, ResourceIdSet(ids + kLockedResources, ids + kUsedResources));
// Can be called multiple times while batching is enabled. This happens in
// practice when the same surface is visited using different paths during
// surface aggregation.
resource_provider_->DeclareUsedResourcesFromChild(
child_id, ResourceIdSet(ids + kLockedResources, ids + kUsedResources));
read_locks.clear();
EXPECT_EQ(0u, returned_to_child.size());
}
EXPECT_EQ(kLockedResources, returned_to_child.size());
// Returned resources that were locked share the same sync token.
for (const auto& resource : returned_to_child)
EXPECT_EQ(resource.sync_token, returned_to_child[0].sync_token);
child_resource_provider_->ReceiveReturnsFromParent(
std::move(returned_to_child));
returned_to_child.clear();
// Returns from destroying the child is also batched.
{
DisplayResourceProviderGL::ScopedBatchReturnResources returner(
resource_provider_.get());
resource_provider_->DestroyChild(child_id);
EXPECT_EQ(0u, returned_to_child.size());
}
EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider_->ReceiveReturnsFromParent(
std::move(returned_to_child));
returned_to_child.clear();
EXPECT_CALL(release, Released(_, _)).Times(kTotalResources);
for (const auto& id : ids)
child_resource_provider_->RemoveImportedResource(id);
}
class TextureStateTrackingGLES2Interface : public TestGLES2Interface {
public:
MOCK_METHOD2(BindTexture, void(GLenum target, GLuint texture));
MOCK_METHOD3(TexParameteri, void(GLenum target, GLenum pname, GLint param));
MOCK_METHOD1(WaitSyncTokenCHROMIUM, void(const GLbyte* sync_token));
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 { return next_texture_id_++; }
void RetireTextureId(GLuint) override {}
};
class ResourceProviderTestImportedResourceGLFilters {
public:
static void RunTest(bool mailbox_nearest_neighbor, GLenum sampler_filter) {
auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
TextureStateTrackingGLES2Interface* gl = gl_owned.get();
auto context_provider = TestContextProvider::Create(std::move(gl_owned));
context_provider->BindToCurrentThread();
auto resource_provider =
std::make_unique<DisplayResourceProviderGL>(context_provider.get());
auto child_gl_owned =
std::make_unique<TextureStateTrackingGLES2Interface>();
TextureStateTrackingGLES2Interface* child_gl = child_gl_owned.get();
auto child_context_provider =
TestContextProvider::Create(std::move(child_gl_owned));
child_context_provider->BindToCurrentThread();
auto child_resource_provider = std::make_unique<ClientResourceProvider>();
unsigned texture_id = 1;
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x12),
0x34);
EXPECT_CALL(*child_gl, BindTexture(_, _)).Times(0);
EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0);
EXPECT_CALL(*child_gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate();
GLuint filter = mailbox_nearest_neighbor ? GL_NEAREST : GL_LINEAR;
constexpr gfx::Size size(64, 64);
auto resource = TransferableResource::MakeGL(
gpu_mailbox, filter, GL_TEXTURE_2D, sync_token, size,
false /* is_overlay_candidate */);
MockReleaseCallback release;
ResourceId resource_id = child_resource_provider->ImportResource(
resource, base::BindOnce(&MockReleaseCallback::Released,
base::Unretained(&release)));
EXPECT_NE(kInvalidResourceId, resource_id);
testing::Mock::VerifyAndClearExpectations(child_gl);
// Transfer resources to the parent.
std::vector<TransferableResource> send_to_parent;
std::vector<ReturnedResource> returned_to_child;
int child_id = resource_provider->CreateChild(
base::BindRepeating(&CollectResources, &returned_to_child),
SurfaceId());
child_resource_provider->PrepareSendToParent(
{resource_id}, &send_to_parent,
static_cast<RasterContextProvider*>(child_context_provider.get()));
resource_provider->ReceiveFromChild(child_id, send_to_parent);
// In DisplayResourceProvider's namespace, use the mapped resource id.
std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
resource_provider->GetChildToParentMap(child_id);
ResourceId mapped_resource_id = resource_map[resource_id];
{
// The verified flush flag will be set by
// ClientResourceProvider::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(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token)));
resource_provider->WaitSyncToken(mapped_resource_id);
testing::Mock::VerifyAndClearExpectations(gl);
EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(texture_id));
EXPECT_CALL(*gl, BindTexture(GL_TEXTURE_2D, texture_id));
// The sampler will reset these if |mailbox_nearest_neighbor| does not
// match |sampler_filter|.
if (mailbox_nearest_neighbor != (sampler_filter == GL_NEAREST)) {
EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
sampler_filter));
EXPECT_CALL(*gl, TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
sampler_filter));
}
DisplayResourceProviderGL::ScopedSamplerGL lock(
resource_provider.get(), mapped_resource_id, sampler_filter);
testing::Mock::VerifyAndClearExpectations(gl);
// When done with it, a sync point should be inserted, but no produce is
// necessary.
EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0);
EXPECT_CALL(*child_gl, 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, ResourceIdSet());
EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider->ReceiveReturnsFromParent(
std::move(returned_to_child));
gpu::SyncToken released_sync_token;
{
EXPECT_CALL(release, Released(_, false))
.WillOnce(SaveArg<0>(&released_sync_token));
child_resource_provider->RemoveImportedResource(resource_id);
}
EXPECT_TRUE(released_sync_token.HasData());
}
};
TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_LinearToLinear) {
ResourceProviderTestImportedResourceGLFilters::RunTest(false, GL_LINEAR);
}
TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_NearestToNearest) {
ResourceProviderTestImportedResourceGLFilters::RunTest(true, GL_NEAREST);
}
TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_NearestToLinear) {
ResourceProviderTestImportedResourceGLFilters::RunTest(true, GL_LINEAR);
}
TEST_F(DisplayResourceProviderGLTest, ReceiveGLTexture2D_LinearToNearest) {
ResourceProviderTestImportedResourceGLFilters::RunTest(false, GL_NEAREST);
}
TEST_F(DisplayResourceProviderGLTest, ReceiveGLTextureExternalOES) {
auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
TextureStateTrackingGLES2Interface* gl = gl_owned.get();
auto context_provider = TestContextProvider::Create(std::move(gl_owned));
context_provider->BindToCurrentThread();
auto resource_provider =
std::make_unique<DisplayResourceProviderGL>(context_provider.get());
auto child_gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
TextureStateTrackingGLES2Interface* child_gl = child_gl_owned.get();
auto child_context_provider =
TestContextProvider::Create(std::move(child_gl_owned));
child_context_provider->BindToCurrentThread();
auto child_resource_provider = std::make_unique<ClientResourceProvider>();
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
EXPECT_CALL(*child_gl, BindTexture(_, _)).Times(0);
EXPECT_CALL(*child_gl, WaitSyncTokenCHROMIUM(_)).Times(0);
EXPECT_CALL(*child_gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
gpu::Mailbox gpu_mailbox = gpu::Mailbox::Generate();
ReleaseCallback callback = base::DoNothing();
constexpr gfx::Size size(64, 64);
auto resource = TransferableResource::MakeGL(
gpu_mailbox, GL_LINEAR, GL_TEXTURE_EXTERNAL_OES, sync_token, size,
false /* is_overlay_candidate */);
ResourceId resource_id =
child_resource_provider->ImportResource(resource, std::move(callback));
EXPECT_NE(kInvalidResourceId, resource_id);
testing::Mock::VerifyAndClearExpectations(child_gl);
// Transfer resources to the parent.
std::vector<TransferableResource> send_to_parent;
std::vector<ReturnedResource> returned_to_child;
int child_id = resource_provider->CreateChild(
base::BindRepeating(&CollectResources, &returned_to_child), SurfaceId());
child_resource_provider->PrepareSendToParent(
{resource_id}, &send_to_parent,
static_cast<RasterContextProvider*>(child_context_provider_.get()));
resource_provider->ReceiveFromChild(child_id, send_to_parent);
// Before create DrawQuad in DisplayResourceProvider's namespace, get the
// mapped resource id first.
std::unordered_map<ResourceId, ResourceId, ResourceIdHasher> resource_map =
resource_provider->GetChildToParentMap(child_id);
ResourceId mapped_resource_id = resource_map[resource_id];
{
// The verified flush flag will be set by
// ClientResourceProvider::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(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token)));
resource_provider->WaitSyncToken(mapped_resource_id);
testing::Mock::VerifyAndClearExpectations(gl);
unsigned texture_id = 1;
EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_))
.WillOnce(Return(texture_id));
DisplayResourceProviderGL::ScopedReadLockGL lock(resource_provider.get(),
mapped_resource_id);
testing::Mock::VerifyAndClearExpectations(gl);
// When done with it, a sync point should be inserted, but no produce is
// necessary.
EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
testing::Mock::VerifyAndClearExpectations(gl);
}
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, ResourceIdSet());
EXPECT_EQ(1u, returned_to_child.size());
child_resource_provider->ReceiveReturnsFromParent(
std::move(returned_to_child));
child_resource_provider->RemoveImportedResource(resource_id);
}
TEST_F(DisplayResourceProviderGLTest, WaitSyncTokenIfNeeded) {
auto gl_owned = std::make_unique<TextureStateTrackingGLES2Interface>();
TextureStateTrackingGLES2Interface* gl = gl_owned.get();
auto context_provider = TestContextProvider::Create(std::move(gl_owned));
context_provider->BindToCurrentThread();
auto resource_provider =
std::make_unique<DisplayResourceProviderGL>(context_provider.get());
EXPECT_CALL(*gl, BindTexture(_, _)).Times(0);
EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
EXPECT_CALL(*gl, CreateAndConsumeTextureCHROMIUM(_)).Times(0);
gpu::SyncToken sync_token(gpu::CommandBufferNamespace::GPU_IO,
gpu::CommandBufferId::FromUnsafeValue(0x12), 0x34);
ResourceId id_with_sync = MakeGpuResourceAndSendToDisplay(
GL_LINEAR, GL_TEXTURE_2D, sync_token, resource_provider.get());
ResourceId id_without_sync = MakeGpuResourceAndSendToDisplay(
GL_LINEAR, GL_TEXTURE_2D, gpu::SyncToken(), resource_provider.get());
// First call to WaitSyncToken should call WaitSyncToken, but only if a
// SyncToken was present.
{
EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(MatchesSyncToken(sync_token)))
.Times(1);
resource_provider->WaitSyncToken(id_with_sync);
EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
resource_provider->WaitSyncToken(id_without_sync);
}
{
// Subsequent calls to WaitSyncToken shouldn't call WaitSyncToken.
EXPECT_CALL(*gl, WaitSyncTokenCHROMIUM(_)).Times(0);
resource_provider->WaitSyncToken(id_with_sync);
resource_provider->WaitSyncToken(id_without_sync);
}
}
} // namespace
} // namespace viz