#ifndef GPU_COMMAND_BUFFER_CLIENT_INTERNAL_MAPPABLE_BUFFER_TEST_TEMPLATE_H_
#define GPU_COMMAND_BUFFER_CLIENT_INTERNAL_MAPPABLE_BUFFER_TEST_TEMPLATE_H_
#include <stddef.h>
#include <string.h>
#include <memory>
#include "base/compiler_specific.h"
#include "base/containers/heap_array.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "build/build_config.h"
#include "components/viz/common/resources/shared_image_format_utils.h"
#include "components/viz/test/test_gpu_service_holder.h"
#include "gpu/command_buffer/client/internal/mappable_buffer_shared_memory.h"
#include "mojo/public/cpp/base/shared_memory_mojom_traits.h"
#include "mojo/public/cpp/test_support/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/buffer_usage_util.h"
#include "ui/gfx/mojom/buffer_types.mojom.h"
#include "ui/gl/gl_display.h"
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_OZONE)
#include "ui/gl/init/gl_factory.h"
#include "ui/gl/test/gl_surface_test_support.h"
#include "ui/ozone/public/ozone_platform.h"
#endif
#if BUILDFLAG(IS_OZONE)
#include "gpu/command_buffer/client/internal/mappable_buffer_native_pixmap.h"
#include "ui/ozone/public/client_native_pixmap_factory_ozone.h"
#include "ui/ozone/public/ozone_platform.h"
#endif
#if BUILDFLAG(IS_MAC)
#include "gpu/command_buffer/client/internal/mappable_buffer_io_surface.h"
#endif
#if BUILDFLAG(IS_OZONE)
#include "gpu/command_buffer/client/internal/mappable_buffer_native_pixmap.h"
#include "ui/ozone/public/client_native_pixmap_factory_ozone.h"
#include "ui/ozone/public/ozone_platform.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "gpu/command_buffer/client/internal/mappable_buffer_dxgi.h"
#endif
#if BUILDFLAG(IS_ANDROID)
#include "gpu/command_buffer/client/internal/mappable_buffer_ahb.h"
#endif
namespace gpu {
template <typename MappableBufferType>
class MappableBufferTest : public testing::Test {
public:
MappableBufferTest() {
#if BUILDFLAG(IS_OZONE)
client_native_pixmap_factory_ = ui::CreateClientNativePixmapFactoryOzone();
#endif
}
void CreateGpuMemoryBuffer(const gfx::Size& size,
viz::SharedImageFormat format,
gfx::BufferUsage usage,
gfx::GpuMemoryBufferHandle* handle) {
MappableBufferType::AllocateForTesting(size, format, usage, handle);
}
std::unique_ptr<MappableBuffer> CreateMappableBufferFromHandle(
gfx::GpuMemoryBufferHandle handle,
const gfx::Size& size,
viz::SharedImageFormat format,
gfx::BufferUsage usage) {
switch (handle.type) {
case gfx::SHARED_MEMORY_BUFFER:
return MappableBufferSharedMemory::CreateFromHandleForTesting(
std::move(handle), size, format, usage);
#if BUILDFLAG(IS_MAC)
case gfx::IO_SURFACE_BUFFER:
return MappableBufferIOSurface::CreateFromHandleForTesting(
std::move(handle), size, format, usage);
#endif
#if BUILDFLAG(IS_OZONE)
case gfx::NATIVE_PIXMAP:
return MappableBufferNativePixmap::CreateFromHandleForTesting(
client_native_pixmap_factory_.get(), std::move(handle), size,
format, usage);
#endif
#if BUILDFLAG(IS_WIN)
case gfx::DXGI_SHARED_HANDLE:
return MappableBufferDXGI::CreateFromHandleForTesting(std::move(handle),
size, format);
#endif
#if BUILDFLAG(IS_ANDROID)
case gfx::ANDROID_HARDWARE_BUFFER:
return MappableBufferAHB::CreateFromHandleForTesting(std::move(handle),
size, format);
#endif
default:
NOTREACHED();
}
}
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_OZONE)
void SetUp() override {
#if BUILDFLAG(IS_OZONE)
if (ui::OzonePlatform::GetPlatformNameForTest() == "wayland") {
run_gpu_test_ = true;
}
#endif
if (run_gpu_test_) {
#if BUILDFLAG(IS_OZONE)
ui::OzonePlatform::InitParams params;
params.single_process = true;
ui::OzonePlatform::InitializeForUI(params);
ui::OzonePlatform::InitializeForGPU(params);
#endif
}
display_ = gl::GLSurfaceTestSupport::InitializeOneOff();
if (run_gpu_test_) {
viz::TestGpuServiceHolder::GetInstance();
base::RunLoop().RunUntilIdle();
}
}
void TearDown() override {
if (run_gpu_test_) {
viz::TestGpuServiceHolder::ResetInstance();
}
gl::GLSurfaceTestSupport::ShutdownGL(display_);
}
#endif
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::MainThreadType::UI};
bool CheckGpuMemoryBufferHandle(const gfx::GpuMemoryBufferHandle& handle) {
#if BUILDFLAG(IS_OZONE)
#else
EXPECT_NE(handle.type, gfx::EMPTY_BUFFER);
#endif
return handle.type != gfx::EMPTY_BUFFER;
}
base::span<gfx::BufferUsage> usages() { return usages_; }
base::span<const viz::SharedImageFormat> formats() {
#if BUILDFLAG(IS_OZONE)
return viz::GetMappableSharedImageFormatForTesting();
#else
return formats_;
#endif
}
private:
bool run_gpu_test_ = false;
raw_ptr<gl::GLDisplay> display_ = nullptr;
#if BUILDFLAG(IS_ANDROID)
std::array<gfx::BufferUsage, 2> usages_ = {
gfx::BufferUsage::GPU_READ,
gfx::BufferUsage::SCANOUT,
};
std::array<viz::SharedImageFormat, 1> formats_ = {
viz::MultiPlaneFormat::kNV12,
};
#elif BUILDFLAG(IS_WIN)
std::array<gfx::BufferUsage, 2> usages_ = {
gfx::BufferUsage::GPU_READ,
gfx::BufferUsage::SCANOUT,
};
std::array<viz::SharedImageFormat, 4> formats_ = {
viz::SinglePlaneFormat::kRGBA_8888,
viz::SinglePlaneFormat::kRGBX_8888,
viz::SinglePlaneFormat::kBGRA_8888,
viz::SinglePlaneFormat::kBGRX_8888,
};
#elif BUILDFLAG(IS_APPLE)
std::array<gfx::BufferUsage, 6> usages_ = {
gfx::BufferUsage::GPU_READ,
gfx::BufferUsage::SCANOUT,
gfx::BufferUsage::SCANOUT_CPU_READ_WRITE,
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
gfx::BufferUsage::SCANOUT_FRONT_RENDERING,
gfx::BufferUsage::SCANOUT_VEA_CPU_READ,
};
std::array<viz::SharedImageFormat, 13> formats_ = {
viz::SinglePlaneFormat::kRGBA_8888, viz::SinglePlaneFormat::kRGBX_8888,
viz::SinglePlaneFormat::kBGRA_8888, viz::SinglePlaneFormat::kBGRX_8888,
viz::SinglePlaneFormat::kR_8, viz::SinglePlaneFormat::kRG_88,
viz::SinglePlaneFormat::kR_16, viz::SinglePlaneFormat::kRG_1616,
viz::SinglePlaneFormat::kRGBA_F16, viz::SinglePlaneFormat::kBGRA_1010102,
viz::MultiPlaneFormat::kNV12, viz::MultiPlaneFormat::kNV12A,
viz::MultiPlaneFormat::kP010,
};
#elif BUILDFLAG(IS_OZONE)
std::array<gfx::BufferUsage, 8> usages_ = {
gfx::BufferUsage::GPU_READ,
gfx::BufferUsage::SCANOUT,
gfx::BufferUsage::SCANOUT_CAMERA_READ_WRITE,
gfx::BufferUsage::CAMERA_AND_CPU_READ_WRITE,
gfx::BufferUsage::SCANOUT_CPU_READ_WRITE,
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
gfx::BufferUsage::SCANOUT_VEA_CPU_READ,
gfx::BufferUsage::VEA_READ_CAMERA_AND_CPU_READ_WRITE,
};
#endif
#if BUILDFLAG(IS_OZONE)
std::unique_ptr<gfx::ClientNativePixmapFactory> client_native_pixmap_factory_;
#endif
};
TYPED_TEST_SUITE_P(MappableBufferTest);
TYPED_TEST_P(MappableBufferTest, CreateFromHandle) {
const gfx::Size kBufferSize(8, 8);
for (auto format : TestFixture::formats()) {
for (auto usage : TestFixture::usages()) {
#if BUILDFLAG(IS_OZONE)
if (TypeParam::kBufferType != gfx::SHARED_MEMORY_BUFFER &&
!ui::OzonePlatform::GetInstance()->IsNativePixmapConfigSupported(
viz::SharedImageFormatToBufferFormat(format), usage)) {
continue;
}
#endif
gfx::GpuMemoryBufferHandle handle;
TestFixture::CreateGpuMemoryBuffer(kBufferSize, format, usage, &handle);
if (!TestFixture::CheckGpuMemoryBufferHandle(handle)) {
continue;
}
std::unique_ptr<MappableBuffer> buffer(
TestFixture::CreateMappableBufferFromHandle(
std::move(handle), kBufferSize, format, usage));
ASSERT_TRUE(buffer);
}
}
}
#if !BUILDFLAG(IS_ANDROID)
TYPED_TEST_P(MappableBufferTest, CreateFromHandleSmallBuffer) {
const gfx::Size kBufferSize(8, 8);
for (auto format : TestFixture::formats()) {
for (auto usage : TestFixture::usages()) {
#if BUILDFLAG(IS_OZONE)
if (TypeParam::kBufferType != gfx::SHARED_MEMORY_BUFFER &&
!ui::OzonePlatform::GetInstance()->IsNativePixmapConfigSupported(
viz::SharedImageFormatToBufferFormat(format), usage)) {
continue;
}
#endif
gfx::GpuMemoryBufferHandle handle;
TestFixture::CreateGpuMemoryBuffer(kBufferSize, format, usage, &handle);
if (!TestFixture::CheckGpuMemoryBufferHandle(handle)) {
continue;
}
gfx::Size bogus_size = kBufferSize;
bogus_size.Enlarge(100, 100);
std::unique_ptr<MappableBuffer> buffer(
TestFixture::CreateMappableBufferFromHandle(
std::move(handle), bogus_size, format, usage));
if (buffer) {
ASSERT_FALSE(buffer->Map());
}
}
}
}
TYPED_TEST_P(MappableBufferTest, Map) {
const gfx::Size kBufferSize(4, 4);
if (!base::Contains(TestFixture::usages(),
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE)) {
GTEST_SKIP();
}
for (auto format : TestFixture::formats()) {
#if BUILDFLAG(IS_OZONE)
if (TypeParam::kBufferType != gfx::SHARED_MEMORY_BUFFER &&
!ui::OzonePlatform::GetInstance()->IsNativePixmapConfigSupported(
viz::SharedImageFormatToBufferFormat(format),
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE)) {
continue;
}
#endif
gfx::GpuMemoryBufferHandle handle;
TestFixture::CreateGpuMemoryBuffer(
kBufferSize, format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
&handle);
if (!TestFixture::CheckGpuMemoryBufferHandle(handle)) {
continue;
}
std::unique_ptr<MappableBuffer> buffer(
TestFixture::CreateMappableBufferFromHandle(
std::move(handle), kBufferSize, format,
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE));
ASSERT_TRUE(buffer);
ASSERT_TRUE(buffer->Map());
ASSERT_TRUE(buffer->Map());
buffer->Unmap();
for (int plane = 0; plane < format.NumberOfPlanes(); ++plane) {
const size_t row_size_in_bytes =
viz::SharedMemoryRowSizeForSharedImageFormat(format, plane,
kBufferSize.width())
.value_or(0);
EXPECT_GT(row_size_in_bytes, 0u);
auto data = base::HeapArray<char>::Uninit(row_size_in_bytes);
UNSAFE_TODO(memset(data.data(), 0x2a + plane, row_size_in_bytes));
size_t height = format.GetPlaneSize(plane, kBufferSize).height();
for (size_t y = 0; y < height; ++y) {
UNSAFE_TODO(memcpy(static_cast<char*>(buffer->memory(plane)) +
y * buffer->stride(plane),
data.data(), row_size_in_bytes));
UNSAFE_TODO(
EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) +
y * buffer->stride(plane),
data.data(), row_size_in_bytes)));
}
}
buffer->Unmap();
}
}
TYPED_TEST_P(MappableBufferTest, PersistentMap) {
const gfx::Size kBufferSize(4, 4);
if (!base::Contains(TestFixture::usages(),
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE)) {
GTEST_SKIP();
}
for (auto format : TestFixture::formats()) {
#if BUILDFLAG(IS_OZONE)
if (TypeParam::kBufferType != gfx::SHARED_MEMORY_BUFFER &&
!ui::OzonePlatform::GetInstance()->IsNativePixmapConfigSupported(
viz::SharedImageFormatToBufferFormat(format),
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE)) {
continue;
}
#endif
gfx::GpuMemoryBufferHandle handle;
TestFixture::CreateGpuMemoryBuffer(
kBufferSize, format, gfx::BufferUsage::GPU_READ_CPU_READ_WRITE,
&handle);
if (!TestFixture::CheckGpuMemoryBufferHandle(handle)) {
continue;
}
std::unique_ptr<MappableBuffer> buffer(
TestFixture::CreateMappableBufferFromHandle(
std::move(handle), kBufferSize, format,
gfx::BufferUsage::GPU_READ_CPU_READ_WRITE));
ASSERT_TRUE(buffer);
ASSERT_TRUE(buffer->Map());
int num_planes = format.NumberOfPlanes();
for (int plane = 0; plane < num_planes; ++plane) {
const size_t row_size_in_bytes =
viz::SharedMemoryRowSizeForSharedImageFormat(format, plane,
kBufferSize.width())
.value_or(0);
EXPECT_GT(row_size_in_bytes, 0u);
auto data = base::HeapArray<char>::Uninit(row_size_in_bytes);
UNSAFE_TODO(memset(data.data(), 0x2a + plane, row_size_in_bytes));
size_t height = format.GetPlaneSize(plane, kBufferSize).height();
for (size_t y = 0; y < height; ++y) {
UNSAFE_TODO(memcpy(static_cast<char*>(buffer->memory(plane)) +
y * buffer->stride(plane),
data.data(), row_size_in_bytes));
UNSAFE_TODO(
EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) +
y * buffer->stride(plane),
data.data(), row_size_in_bytes)));
}
}
buffer->Unmap();
ASSERT_TRUE(buffer->Map());
for (int plane = 0; plane < num_planes; ++plane) {
const size_t row_size_in_bytes =
viz::SharedMemoryRowSizeForSharedImageFormat(format, plane,
kBufferSize.width())
.value_or(0);
EXPECT_GT(row_size_in_bytes, 0u);
auto data = base::HeapArray<char>::Uninit(row_size_in_bytes);
UNSAFE_TODO(memset(data.data(), 0x2a + plane, row_size_in_bytes));
size_t height = format.GetPlaneSize(plane, kBufferSize).height();
for (size_t y = 0; y < height; ++y) {
UNSAFE_TODO(
EXPECT_EQ(0, memcmp(static_cast<char*>(buffer->memory(plane)) +
y * buffer->stride(plane),
data.data(), row_size_in_bytes)));
}
}
buffer->Unmap();
}
}
#endif
TYPED_TEST_P(MappableBufferTest, SerializeAndDeserialize) {
const gfx::Size kBufferSize(8, 8);
const gfx::GpuMemoryBufferType kBufferType = TypeParam::kBufferType;
for (auto format : TestFixture::formats()) {
for (auto usage : TestFixture::usages()) {
#if BUILDFLAG(IS_OZONE)
if (TypeParam::kBufferType != gfx::SHARED_MEMORY_BUFFER &&
!ui::OzonePlatform::GetInstance()->IsNativePixmapConfigSupported(
viz::SharedImageFormatToBufferFormat(format), usage)) {
continue;
}
#endif
gfx::GpuMemoryBufferHandle handle;
TestFixture::CreateGpuMemoryBuffer(kBufferSize, format, usage, &handle);
if (!TestFixture::CheckGpuMemoryBufferHandle(handle)) {
continue;
}
gfx::GpuMemoryBufferHandle output_handle;
mojo::test::SerializeAndDeserialize<gfx::mojom::GpuMemoryBufferHandle>(
handle, output_handle);
EXPECT_EQ(output_handle.type, kBufferType);
std::unique_ptr<MappableBuffer> buffer(
TestFixture::CreateMappableBufferFromHandle(
std::move(output_handle), kBufferSize, format, usage));
ASSERT_TRUE(buffer);
}
}
}
REGISTER_TYPED_TEST_SUITE_P(MappableBufferTest,
CreateFromHandle,
#if !BUILDFLAG(IS_ANDROID)
CreateFromHandleSmallBuffer,
Map,
PersistentMap,
#endif
SerializeAndDeserialize);
}
#endif