blob: 2023157f4ac1ae6a366f2cb98a1e917117b96e83 [file]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gpu/command_buffer/client/client_shared_image.h"
#include <GLES2/gl2.h>
#include <GLES2/gl2extchromium.h>
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/client/test_shared_image_interface.h"
#include "gpu/command_buffer/common/shared_image_capabilities.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/ipc/common/exported_shared_image.mojom.h"
#include "gpu/ipc/common/exported_shared_image_mojom_traits.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/gpu_fence.h"
namespace gpu {
namespace {
const gfx::Size kSize(256, 256);
constexpr viz::SharedImageFormat kMultiPlaneFormatsWithHardwareGMBs[4] = {
viz::MultiPlaneFormat::kYV12, viz::MultiPlaneFormat::kNV12,
viz::MultiPlaneFormat::kNV12A, viz::MultiPlaneFormat::kP010};
SharedImageInfo CreateSharedImageInfo(
viz::SharedImageFormat format = viz::SinglePlaneFormat::kRGBA_8888,
SharedImageUsageSet usage = SHARED_IMAGE_USAGE_RASTER_WRITE |
SHARED_IMAGE_USAGE_DISPLAY_READ) {
return SharedImageInfo{format,
kSize,
gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin,
kOpaque_SkAlphaType,
usage,
"ClientSharedImageTest"};
}
} // namespace
TEST(ClientSharedImageTest, ImportUnowned) {
auto mailbox = Mailbox::Generate();
const auto kFormat = viz::SinglePlaneFormat::kRGBA_8888;
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
SharedImageMetadata metadata{kFormat,
kSize,
gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin,
kOpaque_SkAlphaType,
kUsage};
auto client_si = ClientSharedImage::ImportUnowned(ExportedSharedImage(
mailbox, metadata, SyncToken(), /*managed_sync_tokens=*/{},
"ClientSharedImageTest", std::nullopt, std::nullopt, GL_TEXTURE_2D,
/*is_software=*/false));
// Check that the ClientSI's state matches the input parameters.
EXPECT_EQ(client_si->mailbox(), mailbox);
EXPECT_EQ(client_si->format(), kFormat);
EXPECT_EQ(client_si->size(), kSize);
EXPECT_EQ(client_si->usage(), kUsage);
EXPECT_EQ(client_si->GetTextureTarget(),
static_cast<uint32_t>(GL_TEXTURE_2D));
EXPECT_FALSE(client_si->HasHolder());
}
TEST(ClientSharedImageTest,
ExportedSharedImageMojoDeserialization_TextureTargetZero) {
auto mailbox = Mailbox::Generate();
const auto kFormat = viz::SinglePlaneFormat::kRGBA_8888;
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
SharedImageMetadata metadata{kFormat,
kSize,
gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin,
kOpaque_SkAlphaType,
kUsage};
ExportedSharedImage exported_si(
mailbox, metadata, SyncToken(), /*managed_sync_tokens=*/{},
"ClientSharedImageTest", std::nullopt, std::nullopt,
/*texture_target=*/0, /*is_software=*/false);
ExportedSharedImage deserialized_si;
bool success =
mojo::test::SerializeAndDeserialize<gpu::mojom::ExportedSharedImage>(
exported_si, deserialized_si);
#if BUILDFLAG(IS_FUCHSIA)
EXPECT_TRUE(success);
EXPECT_EQ(deserialized_si.texture_target_, 0u);
#else
EXPECT_FALSE(success);
#endif
}
TEST(ClientSharedImageTest,
ExportedSharedImageMojoDeserialization_EmptyBuffer) {
auto mailbox = Mailbox::Generate();
const auto kFormat = viz::SinglePlaneFormat::kRGBA_8888;
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
SharedImageMetadata metadata{kFormat,
kSize,
gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin,
kOpaque_SkAlphaType,
kUsage};
gfx::GpuMemoryBufferHandle empty_handle;
empty_handle.type = gfx::EMPTY_BUFFER;
ExportedSharedImage exported_si(
mailbox, metadata, SyncToken(), /*managed_sync_tokens=*/{},
"ClientSharedImageTest", std::move(empty_handle),
gfx::BufferUsage::GPU_READ,
/*texture_target=*/GL_TEXTURE_2D, /*is_software=*/false);
ExportedSharedImage deserialized_si;
bool success =
mojo::test::SerializeAndDeserialize<gpu::mojom::ExportedSharedImage>(
exported_si, deserialized_si);
EXPECT_FALSE(success);
}
TEST(ClientSharedImageTest,
ExportedSharedImageMojoDeserialization_ZeroMailbox) {
gpu::Mailbox mailbox;
const auto kFormat = viz::SinglePlaneFormat::kRGBA_8888;
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
SharedImageMetadata metadata{kFormat,
kSize,
gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin,
kOpaque_SkAlphaType,
kUsage};
ExportedSharedImage exported_si(
mailbox, metadata, SyncToken(), /*managed_sync_tokens=*/{},
"ClientSharedImageTest", std::nullopt, std::nullopt,
/*texture_target=*/GL_TEXTURE_2D, /*is_software=*/false);
ExportedSharedImage deserialized_si;
bool success =
mojo::test::SerializeAndDeserialize<gpu::mojom::ExportedSharedImage>(
exported_si, deserialized_si);
EXPECT_FALSE(success);
}
TEST(ClientSharedImageTest,
ExportedSharedImageMojoDeserialization_DuplicateManagedSyncTokens) {
auto mailbox = Mailbox::Generate();
const auto kFormat = viz::SinglePlaneFormat::kRGBA_8888;
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
SharedImageMetadata metadata{kFormat,
kSize,
gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin,
kOpaque_SkAlphaType,
kUsage};
CommandBufferNamespace ns = CommandBufferNamespace::GPU_IO;
CommandBufferId cmd_id = CommandBufferId::FromUnsafeValue(42);
// Two sync tokens from the same sequence (same client_id)
SyncToken token1(ns, cmd_id, /*release_count=*/100);
token1.SetVerifyFlush();
SyncToken token2(ns, cmd_id, /*release_count=*/200);
token2.SetVerifyFlush();
ExportedSharedImage exported_si(
mailbox, metadata, SyncToken(), {token1, token2}, "ClientSharedImageTest",
std::nullopt, std::nullopt,
/*texture_target=*/GL_TEXTURE_2D, /*is_software=*/false);
ExportedSharedImage deserialized_si;
bool success =
mojo::test::SerializeAndDeserialize<gpu::mojom::ExportedSharedImage>(
exported_si, deserialized_si);
// Deserialization must fail because duplicate client IDs are not allowed.
EXPECT_FALSE(success);
}
TEST(ClientSharedImageTest,
ExportedSharedImageMojoDeserialization_ValidManagedSyncTokens) {
auto mailbox = Mailbox::Generate();
const auto kFormat = viz::SinglePlaneFormat::kRGBA_8888;
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
SharedImageMetadata metadata{kFormat,
kSize,
gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin,
kOpaque_SkAlphaType,
kUsage};
CommandBufferNamespace ns = CommandBufferNamespace::GPU_IO;
CommandBufferId cmd_id1 = CommandBufferId::FromUnsafeValue(42);
CommandBufferId cmd_id2 = CommandBufferId::FromUnsafeValue(43);
SyncToken token1(ns, cmd_id1, /*release_count=*/100);
token1.SetVerifyFlush();
SyncToken token2(ns, cmd_id2, /*release_count=*/200);
token2.SetVerifyFlush();
ExportedSharedImage exported_si(
mailbox, metadata, SyncToken(), {token1, token2}, "ClientSharedImageTest",
std::nullopt, std::nullopt,
/*texture_target=*/GL_TEXTURE_2D, /*is_software=*/false);
ExportedSharedImage deserialized_si;
bool success =
mojo::test::SerializeAndDeserialize<gpu::mojom::ExportedSharedImage>(
exported_si, deserialized_si);
EXPECT_TRUE(success);
EXPECT_EQ(deserialized_si.managed_sync_tokens_.size(), 2u);
EXPECT_EQ(deserialized_si.managed_sync_tokens_[0], token1);
EXPECT_EQ(deserialized_si.managed_sync_tokens_[1], token2);
}
TEST(ClientSharedImageTest, CreateMappableBufferFromHandle_EmptyBuffer) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
const auto kFormat = viz::SinglePlaneFormat::kRGBA_8888;
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
SharedImageMetadata metadata{kFormat,
kSize,
gfx::ColorSpace(),
kTopLeft_GrSurfaceOrigin,
kOpaque_SkAlphaType,
kUsage};
gfx::GpuMemoryBufferHandle empty_handle;
empty_handle.type = gfx::EMPTY_BUFFER;
GpuMemoryBufferHandleInfo handle_info(std::move(empty_handle),
gfx::BufferUsage::GPU_READ);
// Creating a ClientSharedImage with an EMPTY_BUFFER handle should not crash.
// The handle will be passed to CreateMappableBufferFromHandle which will
// return nullptr, and the CHECK(mappable_buffer_) will fail, so we expect a
// crash in death tests if we were to proceed, but since it's a CHECK, we can
// test it with EXPECT_DEATH_IF_SUPPORTED.
EXPECT_DEATH_IF_SUPPORTED(
base::MakeRefCounted<ClientSharedImage>(
Mailbox::Generate(), SharedImageInfo{metadata, "TestLabel"},
SyncToken(), std::move(handle_info),
base::MakeRefCounted<SharedImageInterfaceHolder>(sii.get())),
"");
}
TEST(ClientSharedImageTest, CreateViaSharedImageInterface) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
const auto kFormat = viz::SinglePlaneFormat::kRGBA_8888;
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
auto client_si =
sii->CreateSharedImage(CreateSharedImageInfo(), kNullSurfaceHandle);
EXPECT_TRUE(client_si->HasHolder());
EXPECT_FALSE(client_si->mailbox().IsZero());
// Check that the ClientSI's state matches the input parameters.
EXPECT_EQ(client_si->format(), kFormat);
EXPECT_EQ(client_si->size(), kSize);
EXPECT_EQ(client_si->usage(), kUsage);
// With no scanout or WebGPU usage, external sampling not configured, and no
// client-side native buffer handle passed, the SharedImage should be using
// the default texture target on all platforms.
EXPECT_EQ(client_si->GetTextureTarget(),
static_cast<uint32_t>(GL_TEXTURE_2D));
}
TEST(ClientSharedImageTest, BackingWasExternallyUpdatedForwardsToSII) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
auto client_si =
sii->CreateSharedImage(CreateSharedImageInfo(), kNullSurfaceHandle);
ASSERT_EQ(0u, sii->num_update_shared_image_no_fence_calls());
client_si->BackingWasExternallyUpdated(gpu::SyncToken());
EXPECT_EQ(1u, sii->num_update_shared_image_no_fence_calls());
}
// Verifies that invoking BackingWasExternallyUpdated() on a
// ClientSharedImage after its SharedImageInterface has been lost does not cause
// a crash.
TEST(ClientSharedImageTest, BackingWasExternallyUpdatedAfterLossOfSII) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
auto client_si =
sii->CreateSharedImage(CreateSharedImageInfo(), kNullSurfaceHandle);
sii.reset();
client_si->BackingWasExternallyUpdated(gpu::SyncToken());
}
TEST(ClientSharedImageTest, ExportAndImport) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
const auto kFormat = viz::SinglePlaneFormat::kRGBA_8888;
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
auto client_si =
sii->CreateSharedImage(CreateSharedImageInfo(), kNullSurfaceHandle);
auto exported_si = client_si->Export();
auto imported_client_si =
ClientSharedImage::ImportUnowned(std::move(exported_si));
EXPECT_EQ(imported_client_si->mailbox(), client_si->mailbox());
EXPECT_EQ(imported_client_si->format(), kFormat);
EXPECT_EQ(imported_client_si->size(), kSize);
EXPECT_EQ(imported_client_si->usage(), kUsage);
EXPECT_EQ(imported_client_si->GetTextureTarget(),
static_cast<uint32_t>(GL_TEXTURE_2D));
}
TEST(ClientSharedImageTest, MakeUnowned) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
const auto kFormat = viz::SinglePlaneFormat::kRGBA_8888;
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
auto client_si =
sii->CreateSharedImage(CreateSharedImageInfo(), kNullSurfaceHandle);
auto unowned_si = client_si->MakeUnowned();
EXPECT_EQ(unowned_si->mailbox(), client_si->mailbox());
EXPECT_EQ(unowned_si->format(), kFormat);
EXPECT_EQ(unowned_si->size(), kSize);
EXPECT_EQ(unowned_si->usage(), kUsage);
EXPECT_EQ(unowned_si->GetTextureTarget(),
static_cast<uint32_t>(GL_TEXTURE_2D));
EXPECT_FALSE(unowned_si->HasHolder());
}
// The default target should be set for single-planar formats with no
// native buffer used.
TEST(ClientSharedImageTest,
GetTextureTarget_SinglePlaneFormats_NoNativeBuffer) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
for (auto format : viz::SinglePlaneFormat::kAll) {
auto client_si = sii->CreateSharedImage(
CreateSharedImageInfo(format, kUsage), kNullSurfaceHandle);
EXPECT_EQ(client_si->GetTextureTarget(),
static_cast<uint32_t>(GL_TEXTURE_2D));
}
}
// When the client provides a native buffer with a single-plane format,
// GL_TEXTURE_2D should be used as the texture target on all platforms other
// than Mac, where the target for IO surfaces should be used.
TEST(ClientSharedImageTest,
GetTextureTarget_SinglePlaneFormats_ClientNativeBuffer) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
sii->emulate_client_provided_native_buffer();
#if BUILDFLAG(IS_MAC)
// Explicitly set the texture target for IO surfaces to a target other than
// GL_TEXTURE_2D to ensure that the test is meaningful on Mac.
const uint32_t kTargetForIOSurfaces = GL_TEXTURE_RECTANGLE_ARB;
sii->set_texture_target_for_io_surfaces(kTargetForIOSurfaces);
#endif
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
for (auto format : viz::SinglePlaneFormat::kAll) {
auto client_si = sii->CreateSharedImage(
CreateSharedImageInfo(format, kUsage), kNullSurfaceHandle);
#if BUILDFLAG(IS_MAC)
const uint32_t expected_texture_target = kTargetForIOSurfaces;
#else
const uint32_t expected_texture_target = GL_TEXTURE_2D;
#endif
EXPECT_EQ(client_si->GetTextureTarget(), expected_texture_target);
}
}
// When the client asks for SCANOUT usage, GL_TEXTURE_2D should be used as the
// texture target on all platforms other than Mac, where the target for IO
// surfaces should be used.
TEST(ClientSharedImageTest, GetTextureTarget_ScanoutUsage) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
#if BUILDFLAG(IS_MAC)
// Explicitly set the texture target for IO surfaces to a target other than
// GL_TEXTURE_2D to ensure that the test is meaningful on Mac.
const uint32_t kTargetForIOSurfaces = GL_TEXTURE_RECTANGLE_ARB;
sii->set_texture_target_for_io_surfaces(kTargetForIOSurfaces);
#endif
const SharedImageUsageSet kUsage = SHARED_IMAGE_USAGE_SCANOUT;
// Test all single-plane formats as well as multiplane formats for which
// hardware GMBs are supported.
std::vector<viz::SharedImageFormat> formats_to_test;
for (auto format : viz::SinglePlaneFormat::kAll) {
formats_to_test.push_back(format);
}
for (auto format : kMultiPlaneFormatsWithHardwareGMBs) {
formats_to_test.push_back(format);
}
for (auto format : formats_to_test) {
auto client_si = sii->CreateSharedImage(
CreateSharedImageInfo(format, kUsage), kNullSurfaceHandle);
#if BUILDFLAG(IS_MAC)
const uint32_t expected_texture_target = kTargetForIOSurfaces;
#else
const uint32_t expected_texture_target = GL_TEXTURE_2D;
#endif
EXPECT_EQ(client_si->GetTextureTarget(), expected_texture_target);
}
}
// When the client asks for WEBGPU usage, GL_TEXTURE_2D should be used as the
// texture target on all platforms other than Mac, where the target for IO
// surfaces should be used.
TEST(ClientSharedImageTest, GetTextureTarget_WebGPUUsage) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
#if BUILDFLAG(IS_MAC)
// Explicitly set the texture target for IO surfaces to a target other than
// GL_TEXTURE_2D to ensure that the test is meaningful on Mac.
const uint32_t kTargetForIOSurfaces = GL_TEXTURE_RECTANGLE_ARB;
sii->set_texture_target_for_io_surfaces(kTargetForIOSurfaces);
#endif
// Test all single-plane formats as well as multiplane formats for which
// hardware GMBs are supported.
std::vector<viz::SharedImageFormat> formats_to_test;
for (auto format : viz::SinglePlaneFormat::kAll) {
formats_to_test.push_back(format);
}
for (auto format : kMultiPlaneFormatsWithHardwareGMBs) {
formats_to_test.push_back(format);
}
for (SharedImageUsageSet webgpu_usage :
{SHARED_IMAGE_USAGE_WEBGPU_READ, SHARED_IMAGE_USAGE_WEBGPU_WRITE}) {
const SharedImageUsageSet kUsage = webgpu_usage;
for (auto format : formats_to_test) {
auto client_si = sii->CreateSharedImage(
CreateSharedImageInfo(format, kUsage), kNullSurfaceHandle);
#if BUILDFLAG(IS_MAC)
const uint32_t expected_texture_target = kTargetForIOSurfaces;
#else
const uint32_t expected_texture_target = GL_TEXTURE_2D;
#endif
EXPECT_EQ(client_si->GetTextureTarget(), expected_texture_target);
}
}
}
// On all platforms, the default target should be used for multi-planar
// formats if external sampling is not set and scanout/WebGPU usage are not
// specified.
TEST(ClientSharedImageTest,
GetTextureTarget_MultiplanarFormats_NoScanoutOrWebGPUUsage) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
// Pass all the multiplanar formats that are used with hardware GMBs.
for (auto format : kMultiPlaneFormatsWithHardwareGMBs) {
auto client_si = sii->CreateSharedImage(
CreateSharedImageInfo(format, kUsage), kNullSurfaceHandle);
// Since the format does not have external sampling enabled, the default
// target should be used.
EXPECT_EQ(client_si->GetTextureTarget(),
static_cast<uint32_t>(GL_TEXTURE_2D));
}
}
#if BUILDFLAG(IS_OZONE)
// On Ozone, the target for native buffers should be used if a
// multiplanar format with external sampling is passed.
TEST(ClientSharedImageTest,
GetTextureTarget_MultiplanarFormatsWithExternalSampling) {
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
sii->emulate_client_provided_native_buffer();
const SharedImageUsageSet kUsage =
SHARED_IMAGE_USAGE_RASTER_WRITE | SHARED_IMAGE_USAGE_DISPLAY_READ;
// Pass all the multiplanar formats that are used with hardware GMBs.
for (auto format :
{viz::MultiPlaneFormat::kYV12, viz::MultiPlaneFormat::kNV12,
viz::MultiPlaneFormat::kNV12A, viz::MultiPlaneFormat::kP010}) {
format.SetPrefersExternalSampler();
auto client_si = sii->CreateSharedImage(
CreateSharedImageInfo(format, kUsage), kNullSurfaceHandle);
// Since the format has external sampling enabled, the platform-specific
// target for native buffers should be used.
#if BUILDFLAG(IS_FUCHSIA)
EXPECT_EQ(client_si->GetTextureTarget(), 0u);
#else
EXPECT_EQ(client_si->GetTextureTarget(),
static_cast<uint32_t>(GL_TEXTURE_EXTERNAL_OES));
#endif
}
}
#endif
// Checks whether ClientSharedImage correctly ignores input SyncTokens from
// clients and only output empty SyncTokens to clients when feature
// UseAutomaticSyncTokenManagement is enabled.
TEST(ClientSharedImageTest,
AutomaticSyncTokenManagement_DeprecateInputAndOutputSyncTokens) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kUseAutomaticSyncTokenManagement);
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
auto client_si =
sii->CreateSharedImage(CreateSharedImageInfo(), kNullSurfaceHandle);
// 1. Passing an external SyncToken to BackingWasExternallyUpdated should
// return an empty SyncToken to external callers when feature is enabled.
SyncToken dummy_input_token(CommandBufferNamespace::GPU_IO,
CommandBufferId::FromUnsafeValue(123),
/*release_count=*/456);
SyncToken returned_token =
client_si->BackingWasExternallyUpdated(dummy_input_token);
EXPECT_FALSE(returned_token.HasData());
// 2. Exporting and ending export should produce an empty SyncToken to
// external callers.
auto exported_result = client_si->EndImport(dummy_input_token);
SyncToken exported_token = client_si->EndExport(std::move(exported_result));
EXPECT_FALSE(exported_token.HasData());
// 3. EndExportAsVector should return an empty vector to external callers.
auto exported_vec_result =
client_si->EndImport(std::vector<SyncToken>{dummy_input_token});
std::vector<SyncToken> exported_vec =
client_si->EndExportAsVector(std::move(exported_vec_result));
EXPECT_TRUE(exported_vec.empty());
}
// Checks whether ClientSharedImage correctly stores only the latest SyncToken
// on a sequence when feature UseAutomaticSyncTokenManagement is enabled.
TEST(ClientSharedImageTest, AutomaticSyncTokenManagement_SyncTokenUpdate) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kUseAutomaticSyncTokenManagement);
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
auto client_si =
sii->CreateSharedImage(CreateSharedImageInfo(), kNullSurfaceHandle);
CommandBufferNamespace ns = CommandBufferNamespace::GPU_IO;
CommandBufferId cmd_id = CommandBufferId::FromUnsafeValue(42);
// Simulate storing tokens with increasing release counts on sequence (ns,
// cmd_id).
SyncToken token1(ns, cmd_id, /*release_count=*/100);
SyncToken token2(ns, cmd_id, /*release_count=*/200); // Newer
SyncToken token3(ns, cmd_id, /*release_count=*/150); // Older than token2
// EndExport unpacks SharedImageExportResult and invokes
// StoreSyncTokenInternal.
client_si->EndExport(SharedImageExportResult::CreateForTesting(token1));
client_si->EndExport(SharedImageExportResult::CreateForTesting(token2));
client_si->EndExport(SharedImageExportResult::CreateForTesting(token3));
// EndImport exports internal tokens and verifies unverified tokens.
SharedImageExportResult export_result = client_si->EndImport(SyncToken());
EXPECT_TRUE(export_result.HasData());
// The export_result should only contain token2 (verified).
SyncToken expected_token = token2;
expected_token.SetVerifyFlush();
EXPECT_TRUE(export_result.IsEqualForTesting(expected_token));
}
TEST(ClientSharedImageTest, SignalLatestSyncToken_WithCallbackId) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
features::kUseAutomaticSyncTokenManagement);
base::test::SingleThreadTaskEnvironment task_environment;
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
auto client_si =
sii->CreateSharedImage(CreateSharedImageInfo(), kNullSurfaceHandle);
CommandBufferNamespace ns = CommandBufferNamespace::GPU_IO;
CommandBufferId cmd_id = CommandBufferId::FromUnsafeValue(42);
SyncToken token(ns, cmd_id, /*release_count=*/100);
// 1. Invalid sync token (release_count == 0): callback runs immediately.
bool callback_called = false;
uint64_t callback_id = ClientSharedImage::SignalLatestSyncToken(
{client_si}, {SyncToken()},
base::BindOnce([](bool* called) { *called = true; }, &callback_called),
sii.get(), /*pending_callback_id=*/0);
EXPECT_EQ(callback_id, 0u);
EXPECT_TRUE(callback_called);
// 2. Redundant callback (callback_id == pending_callback_id): callback not
// run.
callback_called = false;
callback_id = ClientSharedImage::SignalLatestSyncToken(
{client_si}, {token},
base::BindOnce([](bool* called) { *called = true; }, &callback_called),
sii.get(), /*pending_callback_id=*/100);
EXPECT_EQ(callback_id, 100u);
base::RunLoop run_loop;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
EXPECT_FALSE(callback_called);
// 3. New callback (callback_id != pending_callback_id): callback runs via
// SII.
callback_called = false;
callback_id = ClientSharedImage::SignalLatestSyncToken(
{client_si}, {token},
base::BindOnce([](bool* called) { *called = true; }, &callback_called),
sii.get(), /*pending_callback_id=*/0);
EXPECT_EQ(callback_id, 100u);
EXPECT_FALSE(callback_called);
base::RunLoop run_loop2;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, run_loop2.QuitClosure());
run_loop2.Run();
EXPECT_TRUE(callback_called);
}
TEST(ClientSharedImageTest,
SignalLatestSyncToken_WithCallbackId_AutomaticSyncTokenManagement) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kUseAutomaticSyncTokenManagement);
base::test::SingleThreadTaskEnvironment task_environment;
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
auto client_si =
sii->CreateSharedImage(CreateSharedImageInfo(), kNullSurfaceHandle);
CommandBufferNamespace ns = CommandBufferNamespace::GPU_IO;
CommandBufferId cmd_id = CommandBufferId::FromUnsafeValue(42);
SyncToken token(ns, cmd_id, /*release_count=*/250);
client_si->EndExport(SharedImageExportResult::CreateForTesting(token));
bool callback_called = false;
uint64_t callback_id = ClientSharedImage::SignalLatestSyncToken(
{client_si}, /*sync_tokens=*/{},
base::BindOnce([](bool* called) { *called = true; }, &callback_called),
sii.get(), /*pending_callback_id=*/0);
EXPECT_EQ(callback_id, 250u);
EXPECT_FALSE(callback_called);
base::RunLoop run_loop;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
EXPECT_TRUE(callback_called);
}
TEST(ClientSharedImageTest,
SignalLatestSyncToken_Batch_AutomaticSyncTokenManagement) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(features::kUseAutomaticSyncTokenManagement);
base::test::SingleThreadTaskEnvironment task_environment;
auto sii = base::MakeRefCounted<TestSharedImageInterface>();
auto client_si1 =
sii->CreateSharedImage(CreateSharedImageInfo(), kNullSurfaceHandle);
auto client_si2 =
sii->CreateSharedImage(CreateSharedImageInfo(), kNullSurfaceHandle);
CommandBufferNamespace ns = CommandBufferNamespace::GPU_IO;
CommandBufferId cmd_id1 = CommandBufferId::FromUnsafeValue(10);
CommandBufferId cmd_id2 = CommandBufferId::FromUnsafeValue(20);
SyncToken token1(ns, cmd_id1, /*release_count=*/100);
SyncToken token2(ns, cmd_id2, /*release_count=*/200);
client_si1->EndExport(SharedImageExportResult::CreateForTesting(token1));
client_si2->EndExport(SharedImageExportResult::CreateForTesting(token2));
bool callback_called = false;
ClientSharedImage::SignalLatestSyncToken(
{client_si1, client_si2}, /*sync_tokens=*/{},
base::BindOnce([](bool* called) { *called = true; }, &callback_called),
sii.get());
EXPECT_FALSE(callback_called);
base::RunLoop run_loop;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
EXPECT_TRUE(callback_called);
}
} // namespace gpu